diff --git a/.gitattributes b/.gitattributes index 83ff5ec010933179a64b48537b729f046a953057..ed5fdc651f813acfdf6eb790b9fbbc5fa78a0f50 100644 --- a/.gitattributes +++ b/.gitattributes @@ -849,11 +849,13 @@ CEP/DP3/DPPP/include/DPPP/ScaleData.h -text CEP/DP3/DPPP/include/DPPP/Simulate.h -text CEP/DP3/DPPP/include/DPPP/Simulator.h -text CEP/DP3/DPPP/include/DPPP/SourceDBUtil.h -text +CEP/DP3/DPPP/include/DPPP/StManParsetKeys.h -text CEP/DP3/DPPP/include/DPPP/StefCal.h -text CEP/DP3/DPPP/include/DPPP/Stokes.h -text CEP/DP3/DPPP/include/DPPP/SubtractMixed.h -text CEP/DP3/DPPP/include/DPPP/SubtractNew.h -text CEP/DP3/DPPP/include/DPPP/UVWCalculator.h -text +CEP/DP3/DPPP/include/DPPP/phasefitter.h -text CEP/DP3/DPPP/package.dox -text CEP/DP3/DPPP/share/HBAdefault -text CEP/DP3/DPPP/share/LBAdefault -text @@ -883,6 +885,7 @@ CEP/DP3/DPPP/src/Stokes.cc -text CEP/DP3/DPPP/src/SubtractMixed.cc -text CEP/DP3/DPPP/src/SubtractNew.cc -text CEP/DP3/DPPP/src/__init__.py -text +CEP/DP3/DPPP/src/phasefitter.cc -text CEP/DP3/DPPP/src/taqlflagger -text CEP/DP3/DPPP/test/findenv.run_tmpl -text CEP/DP3/DPPP/test/tApplyBeam.run -text @@ -893,6 +896,8 @@ CEP/DP3/DPPP/test/tApplyCal.cc -text CEP/DP3/DPPP/test/tApplyCal.in_parmdb.tgz -text svneol=unset#application/x-gzip CEP/DP3/DPPP/test/tApplyCal.run -text CEP/DP3/DPPP/test/tApplyCal.sh -text +CEP/DP3/DPPP/test/tApplyCal2.run -text +CEP/DP3/DPPP/test/tApplyCal2.sh -text CEP/DP3/DPPP/test/tApplyCal_parmdbscript -text CEP/DP3/DPPP/test/tDemix.in_MS.tgz -text CEP/DP3/DPPP/test/tDemix.run -text @@ -1312,6 +1317,8 @@ CEP/MS/src/ls_nostderr -text CEP/MS/src/makemsdistr -text CEP/MS/src/makemsdistr-part -text CEP/MS/src/movemss -text +CEP/MS/test/tBaselineSelect.sh -text +CEP/MS/test/tBaselineSelect.stdout -text CEP/MS/test/tcombinevds.in_vds1 -text CEP/MS/test/tcombinevds.in_vds2 -text CEP/MS/test/tmakems.in_antenna.tgz -text svneol=unset#application/x-compressed-tar @@ -1383,6 +1390,7 @@ CEP/Pipeline/docs/examples/model_parsets/dppp4.parset eol=lf CEP/Pipeline/docs/examples/model_parsets/mwimager.parset eol=lf CEP/Pipeline/docs/examples/model_parsets/mwimager.pulsar.parset eol=lf CEP/Pipeline/docs/examples/model_parsets/ndppp.parset eol=lf +CEP/Pipeline/docs/genericpipeline/gendocrestruct.rst -text CEP/Pipeline/docs/notes/2010-11-15-grid.rst eol=lf CEP/Pipeline/docs/notes/2010-12-08-handover_discussion.rst eol=lf CEP/Pipeline/docs/notes/metadata.annotated eol=lf @@ -1515,6 +1523,7 @@ CEP/Pipeline/recipes/examples/master/example.py eol=lf CEP/Pipeline/recipes/examples/master/example_parallel.py eol=lf CEP/Pipeline/recipes/examples/nodes/example_parallel.py eol=lf CEP/Pipeline/recipes/sip/CMakeLists.txt eol=lf +CEP/Pipeline/recipes/sip/bin/CMakeLists.txt -text CEP/Pipeline/recipes/sip/bin/genericpipeline.py -text CEP/Pipeline/recipes/sip/bin/imaging_pipeline.py -text CEP/Pipeline/recipes/sip/bin/loader.py -text @@ -1523,6 +1532,7 @@ CEP/Pipeline/recipes/sip/bin/msss_calibrator_pipeline.py eol=lf CEP/Pipeline/recipes/sip/bin/msss_imager_pipeline.py eol=lf CEP/Pipeline/recipes/sip/bin/msss_target_pipeline.py eol=lf CEP/Pipeline/recipes/sip/bin/pulsar_pipeline.py -text +CEP/Pipeline/recipes/sip/bin/runPipeline.sh eol=lf CEP/Pipeline/recipes/sip/bin/selfcal_imager_pipeline.py eol=lf CEP/Pipeline/recipes/sip/bin/startPython.sh eol=lf CEP/Pipeline/recipes/sip/bin/startPythonVersion.sh -text @@ -1538,6 +1548,7 @@ CEP/Pipeline/recipes/sip/demixing/bbs_TauA.parset eol=lf CEP/Pipeline/recipes/sip/demixing/bbs_TauA_smoothcal.parset eol=lf CEP/Pipeline/recipes/sip/demixing/bbs_VirA.parset eol=lf CEP/Pipeline/recipes/sip/demixing/bbs_VirA_smoothcal.parset eol=lf +CEP/Pipeline/recipes/sip/external/bad_station_detection/CMakeLists.txt -text CEP/Pipeline/recipes/sip/helpers/ComplexArray.py -text CEP/Pipeline/recipes/sip/helpers/MultipartPostHandler.py -text CEP/Pipeline/recipes/sip/helpers/WritableParmDB.py -text @@ -1614,8 +1625,8 @@ CEP/Pipeline/recipes/sip/nodes/selfcal_finalize.py eol=lf CEP/Pipeline/recipes/sip/nodes/setupparmdb.py eol=lf CEP/Pipeline/recipes/sip/nodes/setupsourcedb.py eol=lf CEP/Pipeline/recipes/sip/nodes/vdsmaker.py eol=lf +CEP/Pipeline/recipes/sip/pipeline.cfg.CEP4.tmpl -text CEP/Pipeline/recipes/sip/pipeline.cfg.in eol=lf -CEP/Pipeline/recipes/sip/pipeline.cfg.thead01.cep4 -text CEP/Pipeline/recipes/sip/plugins/PipelineStep_addMapfile.py -text CEP/Pipeline/recipes/sip/plugins/PipelineStep_changeMapfile.py -text CEP/Pipeline/recipes/sip/plugins/PipelineStep_combineParsets.py -text @@ -1629,6 +1640,7 @@ CEP/Pipeline/recipes/sip/skymodels/3C380.skymodel -text CEP/Pipeline/recipes/sip/skymodels/3C48.skymodel eol=lf CEP/Pipeline/recipes/sip/skymodels/Ateam_LBA_CC.skymodel eol=lf CEP/Pipeline/recipes/sip/skymodels/CygA.skymodel -text +CEP/Pipeline/recipes/sip/tasks.cfg.CEP4.in eol=lf CEP/Pipeline/recipes/sip/tasks.cfg.in eol=lf CEP/Pipeline/test/CMakeLists.txt eol=lf CEP/Pipeline/test/__init__.py eol=lf @@ -1671,6 +1683,7 @@ CEP/Pipeline/test/regression_tests/target_pipeline.py -text CEP/Pipeline/test/regression_tests/trunk_imaging_regression.parset -text CEP/Pipeline/test/support/__init__.py eol=lf CEP/Pipeline/test/support/loggingdecorators_test.py -text +CEP/Pipeline/test/support/output_stderr_stdout.sh eol=lf CEP/Pipeline/test/support/subprocessgroup_test.py eol=lf CEP/Pipeline/test/support/xmllogging_test.py -text CEP/Pipeline/test/test_framework/CMakeLists.txt eol=lf @@ -2320,7 +2333,7 @@ CMake/testscripts/checkfloat -text CMake/testscripts/default.debug -text CMake/testscripts/timeout -text CMake/variants/variants.MacRenting -text -CMake/variants/variants.b7015 -text +CMake/variants/variants.buildhostcentos7 -text CMake/variants/variants.cbt001 -text CMake/variants/variants.cbt002 -text CMake/variants/variants.cbt003 -text @@ -2332,27 +2345,34 @@ CMake/variants/variants.cbt008 -text CMake/variants/variants.cbt009 -text CMake/variants/variants.cbt010 -text CMake/variants/variants.dop282 -text +CMake/variants/variants.dop320 -text CMake/variants/variants.dragproc -text -CMake/variants/variants.fs0 -text -CMake/variants/variants.gpu01 -text -CMake/variants/variants.gpu1 -text +CMake/variants/variants.head01 -text +CMake/variants/variants.lcs157 -text CMake/variants/variants.lexar -text CMake/variants/variants.lexar001 -text CMake/variants/variants.lexar002 -text CMake/variants/variants.lhn002 -text CMake/variants/variants.lotar -text -CMake/variants/variants.node501 -text -CMake/variants/variants.node502 -text -CMake/variants/variants.node503 -text -CMake/variants/variants.node521 -text -CMake/variants/variants.phi -text -CMake/variants/variants.sharkbay -text Docker/docker-build-all.sh -text +Docker/dynspec/Dockerfile -text +Docker/dynspec/bashrc -text +Docker/dynspec/chuser.sh -text Docker/lofar-base/Dockerfile.tmpl -text Docker/lofar-base/bashrc -text +Docker/lofar-base/bashrc.d/00-casacore -text +Docker/lofar-base/bashrc.d/00-qpid -text +Docker/lofar-base/bashrc.d/01-python-casacore -text +Docker/lofar-base/bashrc.d/50-lofar -text Docker/lofar-base/chuser.sh -text Docker/lofar-outputproc/Dockerfile.tmpl -text Docker/lofar-pipeline/Dockerfile.tmpl -text +Docker/lofar-pulp/Dockerfile.tmpl -text +Docker/lofar-pulp/bashrc -text +Docker/lofar-pulp/sudoers -text +Docker/lofar-tbbwriter/Dockerfile -text +Docker/lofar-tbbwriter/bashrc -text +Docker/lofar-tbbwriter/chuser.sh -text JAVA/GUI/Plotter/dist/lib/sgt.jar -text svneol=unset#unset JAVA/GUI/Plotter/doc/Plotter.EAP -text JAVA/GUI/Plotter/doc/javadoc/resources/inherit.gif -text @@ -2667,6 +2687,7 @@ LCS/PyCommon/postgres.py -text LCS/PyCommon/test/python-coverage.sh eol=lf LCS/PyCommon/test/t_dbcredentials.run eol=lf LCS/PyCommon/test/t_dbcredentials.sh eol=lf +LCS/PyCommon/test/t_methodtrigger.sh eol=lf LCS/PyCommon/util.py -text LCS/Tools/src/checkcomp.py -text LCS/Tools/src/countalllines -text @@ -2719,6 +2740,7 @@ LCU/PPSTune/doc/source/instructions-menno.rst -text LCU/PPSTune/doc/source/ppstune.rst -text LCU/PPSTune/doc/source/readme.rst -text LCU/PPSTune/ppstune.sh -text svneol=unset#text/x-shellscript +LCU/PPSTune/ppstune/CMakeLists.txt -text LCU/PPSTune/ppstune/__init__.py -text LCU/PPSTune/ppstune/plots.py -text LCU/PPSTune/ppstune/ppstune.py -text @@ -2908,19 +2930,38 @@ LCU/StationTest/xc_160_setup.sh eol=lf LCU/StationTest/xc_160_verify.sh eol=lf LCU/StationTest/xc_200_setup.sh eol=lf LCU/StationTest/xc_200_verify.sh eol=lf -LCU/checkhardware/checkHardware.conf -text -LCU/checkhardware/checkHardware.py -text -LCU/checkhardware/doStationTest.sh -text svneol=unset#application/x-shellscript -LCU/checkhardware/lib/data_lib.py -text -LCU/checkhardware/lib/general_lib.py -text -LCU/checkhardware/lib/lofar_lib.py -text -LCU/checkhardware/lib/search_lib.py -text -LCU/checkhardware/lib/test_db.py -text -LCU/checkhardware/lib/test_lib.py -text +LCU/checkhardware/check_hardware.py -text +LCU/checkhardware/checkhardware_lib/__init__.py -text +LCU/checkhardware/checkhardware_lib/data.py -text +LCU/checkhardware/checkhardware_lib/db.py -text +LCU/checkhardware/checkhardware_lib/general.py -text +LCU/checkhardware/checkhardware_lib/hardware_tests.py -text +LCU/checkhardware/checkhardware_lib/hba.py -text +LCU/checkhardware/checkhardware_lib/lba.py -text +LCU/checkhardware/checkhardware_lib/lofar.py -text +LCU/checkhardware/checkhardware_lib/reporting.py -text +LCU/checkhardware/checkhardware_lib/rsp.py -text +LCU/checkhardware/checkhardware_lib/settings.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/__init__.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/cable_reflection.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/down.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/down_old.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/flat.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/noise.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/oscillation.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/peakslib.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/rf_power.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/short.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/spurious.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/summator_noise.py -text +LCU/checkhardware/checkhardware_lib/spectrum_checks/tools.py -text +LCU/checkhardware/checkhardware_lib/spu.py -text +LCU/checkhardware/checkhardware_lib/tbb.py -text +LCU/checkhardware/do_station_test.sh -text svneol=unset#application/x-shellscript LCU/checkhardware/rtsm.py -text -LCU/checkhardware/showBadSpectra.py -text -LCU/checkhardware/showTestResult.py -text -LCU/checkhardware/updatePVSS.py -text +LCU/checkhardware/show_bad_spectra.py -text +LCU/checkhardware/show_test_result.py -text +LCU/checkhardware/update_pvss.py -text LTA/LTAIngest/ClientForm-0.1.17/ClientForm-0.1.17/PKG-INFO -text LTA/LTAIngest/ClientForm-0.1.17/PKG-INFO -text LTA/LTAIngest/SOAPpy-0.12.0/LICENSE -text @@ -2941,6 +2982,8 @@ LTA/LTAIngest/ltaingest_build.sh eol=lf LTA/LTAIngest/md5adler/a32 -text LTA/LTAIngest/md5adler/foo -text LTA/LTAIngest/md5adler/md5a32 -text +LTA/LTAIngest/md5adler/md5a32bc -text +LTA/LTAIngest/md5adler/md5a32bc.c -text LTA/LTAIngest/mechanize-0.2.5/PKG-INFO -text LTA/LTAIngest/mechanize-0.2.5/examples/forms/data.dat -text LTA/LTAIngest/mechanize-0.2.5/examples/forms/echo.cgi -text @@ -3457,8 +3500,12 @@ MAC/Deployment/data/OTDB/genArrayC++.py -text MAC/Deployment/data/OTDB/genArrayJava.py -text MAC/Deployment/data/OTDB/genArrayTable.py -text MAC/Deployment/data/OTDB/genArrayTest.py -text +MAC/Deployment/data/PVSS/License/471_3031_1_Astron_Gen_I_3_314.log -text MAC/Deployment/data/PVSS/License/471_3031_2_Astron_Gen_II_2_311.log -text +MAC/Deployment/data/PVSS/License/471_3031_2_Astron_Gen_II_3_314.log -text +MAC/Deployment/data/PVSS/License/CCU002_option.txt -text MAC/Deployment/data/PVSS/License/CCU099_option.txt -text +MAC/Deployment/data/PVSS/License/CCU199_option.txt -text MAC/Deployment/data/PVSS/License/CS001C_option_lcu037.txt -text MAC/Deployment/data/PVSS/License/CS002_option.txt -text MAC/Deployment/data/PVSS/License/CS003C_option.txt -text @@ -3484,6 +3531,7 @@ MAC/Deployment/data/PVSS/License/CS103C_option.txt -text MAC/Deployment/data/PVSS/License/CS201C_option.txt -text MAC/Deployment/data/PVSS/License/CS301C_option.txt -text MAC/Deployment/data/PVSS/License/CS302C_option.txt -text +MAC/Deployment/data/PVSS/License/CS302N_option.txt -text MAC/Deployment/data/PVSS/License/CS401C_option.lcu044.txt -text MAC/Deployment/data/PVSS/License/CS401C_option.txt -text MAC/Deployment/data/PVSS/License/CS501C_option.txt -text @@ -3502,7 +3550,9 @@ MAC/Deployment/data/PVSS/License/HwCode.cs016c -text MAC/Deployment/data/PVSS/License/HwCode.mcu001 -text MAC/Deployment/data/PVSS/License/License[!!-~]administration.xlsx -text MAC/Deployment/data/PVSS/License/MCU001_option.old.txt -text +MAC/Deployment/data/PVSS/License/MCU002_option.txt -text MAC/Deployment/data/PVSS/License/MCU099_option.txt -text +MAC/Deployment/data/PVSS/License/MCU199_option.txt -text MAC/Deployment/data/PVSS/License/PL610C_option.txt -text MAC/Deployment/data/PVSS/License/PL611C_option.txt -text MAC/Deployment/data/PVSS/License/PL612C_option.txt -text @@ -3527,7 +3577,9 @@ MAC/Deployment/data/PVSS/License/RS509C_option.txt -text MAC/Deployment/data/PVSS/License/SE607C_option.txt -text MAC/Deployment/data/PVSS/License/Station.rtu -text MAC/Deployment/data/PVSS/License/UK608C_option.txt -text +MAC/Deployment/data/PVSS/License/shield.CCU002.txt -text MAC/Deployment/data/PVSS/License/shield.CCU099.txt -text +MAC/Deployment/data/PVSS/License/shield.CCU199.txt -text MAC/Deployment/data/PVSS/License/shield.CS001C.txt -text MAC/Deployment/data/PVSS/License/shield.CS001C_lcu037.txt -text MAC/Deployment/data/PVSS/License/shield.CS002C.txt -text @@ -3554,6 +3606,7 @@ MAC/Deployment/data/PVSS/License/shield.CS103C.txt -text MAC/Deployment/data/PVSS/License/shield.CS201C.txt -text MAC/Deployment/data/PVSS/License/shield.CS301C.txt -text MAC/Deployment/data/PVSS/License/shield.CS302C.txt -text +MAC/Deployment/data/PVSS/License/shield.CS302N.txt -text MAC/Deployment/data/PVSS/License/shield.CS401C.txt -text MAC/Deployment/data/PVSS/License/shield.CS401C_lcu044.txt -text MAC/Deployment/data/PVSS/License/shield.CS501C.txt -text @@ -3565,7 +3618,9 @@ MAC/Deployment/data/PVSS/License/shield.DE605C.txt -text MAC/Deployment/data/PVSS/License/shield.DE609C.txt -text MAC/Deployment/data/PVSS/License/shield.FR606C.txt -text MAC/Deployment/data/PVSS/License/shield.FR606C_lcu101.txt -text +MAC/Deployment/data/PVSS/License/shield.MCU002.txt -text MAC/Deployment/data/PVSS/License/shield.MCU099.txt -text +MAC/Deployment/data/PVSS/License/shield.MCU199.txt -text MAC/Deployment/data/PVSS/License/shield.PL610C.txt -text MAC/Deployment/data/PVSS/License/shield.PL611C.txt -text MAC/Deployment/data/PVSS/License/shield.PL612C.txt -text @@ -3604,7 +3659,9 @@ MAC/Deployment/data/PVSS/data/LocusNode.dpdef -text MAC/Deployment/data/PVSS/data/MCUbase.dpdef -text MAC/Deployment/data/PVSS/data/Observation.dpdef -text MAC/Deployment/data/PVSS/data/ObservationControl.dpdef -text +MAC/Deployment/data/PVSS/data/POWEC.list -text MAC/Deployment/data/PVSS/data/PVSSbase.dpdef -text +MAC/Deployment/data/PVSS/data/PowerUnit.dpdef -text MAC/Deployment/data/PVSS/data/RTDBPort.dpdef -text MAC/Deployment/data/PVSS/data/SoftwareMonitor.dpdef -text MAC/Deployment/data/PVSS/data/StationControl.dpdef -text @@ -3830,7 +3887,10 @@ MAC/Navigator2/config/config.level.ccu -text MAC/Navigator2/config/config.level.maincu -text MAC/Navigator2/config/config.level.station -text MAC/Navigator2/config/config.maincu -text +MAC/Navigator2/config/config.navigator.3.10 -text +MAC/Navigator2/config/config.navigator.3.14 -text MAC/Navigator2/config/config.sas099 -text +MAC/Navigator2/config/config.smu002 -text MAC/Navigator2/config/config.standalone.station -text MAC/Navigator2/config/progs.ccu -text MAC/Navigator2/config/progs.dist.station -text @@ -3864,6 +3924,7 @@ MAC/Navigator2/panels/Hardware/RemoteOverview.pnl -text MAC/Navigator2/panels/Hardware/Station.pnl -text MAC/Navigator2/panels/Hardware/Station_Cabinet.pnl -text MAC/Navigator2/panels/Hardware/Station_Cabinet_detailed.pnl -text +MAC/Navigator2/panels/Hardware/Station_PowerUnits.pnl -text MAC/Navigator2/panels/Hardware/Station_RCU.pnl -text MAC/Navigator2/panels/Hardware/Station_RSPBoard.pnl -text MAC/Navigator2/panels/Hardware/Station_Subrack.pnl -text @@ -3919,26 +3980,36 @@ MAC/Navigator2/panels/Reports/LOFAR_System.pnl -text MAC/Navigator2/panels/Reports/LOFAR_WebBasePanel.pnl -text MAC/Navigator2/panels/Settings/mail.pnl -text MAC/Navigator2/panels/Settings/mail.pnl.bak -text +MAC/Navigator2/panels/Test/Claim_Viewer.pnl -text +MAC/Navigator2/panels/Test/CobaltTestStub.pnl -text MAC/Navigator2/panels/Test/Event_Viewer.pnl -text +MAC/Navigator2/panels/Test/Navigator_testPanel.pnl -text MAC/Navigator2/panels/Test/stringTest.pnl -text MAC/Navigator2/panels/Test/test[!!-~]colorrange.pnl -text MAC/Navigator2/panels/Test/test.pnl -text MAC/Navigator2/panels/Test/test2.pnl -text +MAC/Navigator2/panels/Test/test3.pnl -text +MAC/Navigator2/panels/Test/testAlertRowLOFAR.pnl -text MAC/Navigator2/panels/Test/testClaim.pnl -text MAC/Navigator2/panels/Test/testColorFade.pnl -text MAC/Navigator2/panels/Test/testDoubleClick.pnl -text +MAC/Navigator2/panels/Test/testDynDynSort.pnl -text MAC/Navigator2/panels/Test/testDynamicPlacement.pnl -text +MAC/Navigator2/panels/Test/testEWO.pnl -text MAC/Navigator2/panels/Test/testExist.pnl -text MAC/Navigator2/panels/Test/testHBA.pnl -text MAC/Navigator2/panels/Test/testStatesetter.pnl -text +MAC/Navigator2/panels/Test/testStationSumAlertToMCUalert.pnl -text MAC/Navigator2/panels/Test/testclick.pnl -text MAC/Navigator2/panels/Test/testdpnames.pnl -text +MAC/Navigator2/panels/Test/testdptypename.pnl -text MAC/Navigator2/panels/Test/testhtml.pnl -text MAC/Navigator2/panels/emptyPanel.pnl -text MAC/Navigator2/panels/main.pnl -text MAC/Navigator2/panels/navigator.pnl -text MAC/Navigator2/panels/nopanel.pnl -text MAC/Navigator2/panels/objects/Alerts/alarms.pnl -text +MAC/Navigator2/panels/objects/Alerts/alarmsWinCCOA.pnl -text MAC/Navigator2/panels/objects/FRENKM_STATION.pnl -text MAC/Navigator2/panels/objects/Hardware/AARTFAAC-RSP.pnl -text MAC/Navigator2/panels/objects/Hardware/AARTFAAC-UNIBoard.pnl -text @@ -3990,6 +4061,7 @@ MAC/Navigator2/panels/objects/Hardware/Station_Cabinet_top.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_Clock.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_HBA.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_LBA.pnl -text +MAC/Navigator2/panels/objects/Hardware/Station_PowerUnit.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_Subrack_small.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_TempAndHumidity.pnl -text MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl -text @@ -4015,6 +4087,7 @@ MAC/Navigator2/panels/objects/Hardware/diskuse_small.pnl -text MAC/Navigator2/panels/objects/Hardware/lofar_HW_state.pnl -text MAC/Navigator2/panels/objects/Hardware/memuse_small.pnl -text MAC/Navigator2/panels/objects/Hardware/observationFlow_stations.pnl -text +MAC/Navigator2/panels/objects/Hardware/powerUnit_small.pnl -text MAC/Navigator2/panels/objects/Hardware/superterpStatusView.pnl -text MAC/Navigator2/panels/objects/Observations/Observation_small.pnl -text MAC/Navigator2/panels/objects/Observations/Pipeline_small.pnl -text @@ -4072,6 +4145,7 @@ MAC/Navigator2/panels/objects/navigator_viewSelection.pnl -text MAC/Navigator2/panels/objects/show_legenda.pnl -text MAC/Navigator2/panels/objects/swlevel.pnl -text MAC/Navigator2/panels/objects/systemMainLine.pnl -text +MAC/Navigator2/panels/vision/aes/AES_properties.pnl -text MAC/Navigator2/pictures/16_empty.gif -text svneol=unset#image/gif MAC/Navigator2/pictures/16_hand_right.gif -text svneol=unset#image/gif MAC/Navigator2/pictures/253.bmp -text svneol=unset#image/bmp @@ -4139,7 +4213,11 @@ MAC/Navigator2/scripts/monitorStateReset.ctl -text MAC/Navigator2/scripts/monitorStationAlarms.ctl -text MAC/Navigator2/scripts/readStationConfigs.ctl -text MAC/Navigator2/scripts/readStationConnections.ctl -text +MAC/Navigator2/scripts/setSumAlerts.ctl -text MAC/Navigator2/scripts/transferMPs.ctl -text +MAC/Services/src/pipelinecontrol -text +MAC/Services/src/pipelinecontrol.ini -text +MAC/Services/test/tPipelineControl.sh eol=lf MAC/Test/APL/PVSSproject/colorDB/Lofar[!!-~]colors -text svneol=native#application/octet-stream MAC/Test/APL/PVSSproject/colorDB/colorDB_de -text svneol=native#application/octet-stream MAC/Test/APL/PVSSproject/config/config -text svneol=native#application/octet-stream @@ -4310,6 +4388,8 @@ MAC/Tools/NTP/timer.c -text MAC/Tools/NTP/timer.c.patch -text MAC/Tools/NTP/timex.h -text MAC/Tools/Power/ecSetObserving.py -text +MAC/Tools/Power/ec_reset_trip.py -text +MAC/Tools/Power/ec_set_observing.py -text MAC/Tools/Power/isEcLib.py -text MAC/Tools/Power/isReset48V.py -text MAC/Tools/Power/isResetLCU.py -text @@ -4319,10 +4399,18 @@ MAC/Tools/Power/isStatusData.py -text MAC/Tools/Power/isTurnOff48V.py -text MAC/Tools/Power/isTurnOffLCU.py -text MAC/Tools/Power/isTurnOn48V.py -text -MAC/Tools/Power/isTurnOnLCU.py -text MAC/Tools/Power/nlEcLib.py -text MAC/Tools/Power/nlStatus.py -text MAC/Tools/Power/nlStatusData.py -text +MAC/Tools/Power/reset_48v.py -text +MAC/Tools/Power/reset_lcu.py -text +MAC/Tools/Power/st_ec_lib.py -text +MAC/Tools/Power/status.py -text +MAC/Tools/Power/status_data.py -text +MAC/Tools/Power/turn_off_48v.py -text +MAC/Tools/Power/turn_off_lcu.py -text +MAC/Tools/Power/turn_on_48v.py -text +MAC/Tools/Power/turn_on_lcu.py -text MAC/Tools/Rubidium/filter.py -text MAC/Tools/Rubidium/rlp.py -text MAC/Tools/Rubidium/rr.py -text @@ -4384,9 +4472,6 @@ RTCP/Cobalt/CobaltTest/test/tMultiPartTABOutput.sh eol=lf RTCP/Cobalt/GPUProc/doc/2ndtranspose.txt -text RTCP/Cobalt/GPUProc/doc/BGP-Cobalt-procs.dia -text RTCP/Cobalt/GPUProc/doc/BGP-Cobalt-procs.png -text svneol=unset#image/png -RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.dia -text -RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.png -text svneol=unset#image/png -RTCP/Cobalt/GPUProc/doc/DAS4-fs5-jenkins-install-instructions.txt -text RTCP/Cobalt/GPUProc/doc/bf-2nd-transpose.dia -text RTCP/Cobalt/GPUProc/doc/bf-2nd-transpose.png -text svneol=unset#image/png RTCP/Cobalt/GPUProc/doc/cobalt-commissioning-report/0034-0534.resids.ft-only-BGP-points.png -text svneol=unset#image/png @@ -4554,6 +4639,7 @@ RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.in_parset -text RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.sh eol=lf RTCP/Cobalt/InputProc/doc/Cobalt-New-InputSection.jpg -text svneol=unset#image/jpeg RTCP/Cobalt/InputProc/src/Delays/printDelays.log_prop -text +RTCP/Cobalt/InputProc/src/ping_intl.sh -text RTCP/Cobalt/InputProc/test/tMPI.run eol=lf RTCP/Cobalt/InputProc/test/tMPI.sh eol=lf RTCP/Cobalt/InputProc/test/tMPIUtil2.run eol=lf @@ -4583,35 +4669,12 @@ RTCP/Cobalt/OpenCL_FFT/src/libOpenCL_FFT.a.not -text RTCP/Cobalt/OpenCL_FFT/src/main.cpp -text RTCP/Cobalt/OpenCL_FFT/src/param.txt -text RTCP/Cobalt/OpenCL_FFT/src/procs.h -text +RTCP/Cobalt/OutputProc/etc/sudoers.d/setcap_cobalt -text RTCP/Cobalt/OutputProc/scripts/bf-output-loss.sh eol=lf RTCP/Cobalt/OutputProc/test/tMSWriterCorrelated_.run.in eol=lf RTCP/Cobalt/OutputProc/test/tMeasurementSetFormat.parset-j2000 -text RTCP/Cobalt/OutputProc/test/tMeasurementSetFormat.parset-sun -text RTCP/Cobalt/OutputProc/test/tTBB_StaticMapping.in_1/TBBConnections.dat -text -RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj -text -RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.user -text -RTCP/Cobalt/VisualStudio/Cobalt.sln -text -RTCP/Cobalt/VisualStudio/Cobalt.suo -text -RTCP/Cobalt/VisualStudio/Cobalt.v12.suo -text -RTCP/Cobalt/VisualStudio/Cobalt.vcxproj -text -RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.user -text -RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj -text -RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.user -text -RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj -text -RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.user -text -RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj -text -RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.user -text -RTCP/Cobalt/VisualStudio/symbolic_links/create_links.bat -text -RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj -text -RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj.user -text -RTCP/Cobalt/VisualStudio/test/test.vcxproj -text -RTCP/Cobalt/VisualStudio/test/test.vcxproj.filters -text -RTCP/Cobalt/VisualStudio/test/test.vcxproj.user -text RTCP/Cobalt/clAmdFft/appmlEnv.sh -text RTCP/Cobalt/clAmdFft/bin32/clAmdFft.Client -text RTCP/Cobalt/clAmdFft/bin32/clAmdFft.Client-1.8.291 -text @@ -4714,6 +4777,39 @@ SAS/CleanupTool/src/schedulersettings.h -text SAS/CleanupTool/src/schedulesettingsdialog.cpp -text SAS/CleanupTool/src/schedulesettingsdialog.h -text SAS/CleanupTool/src/schedulesettingsdialog.ui -text +SAS/DataManagement/CMakeLists.txt -text +SAS/DataManagement/CleanupService/CMakeLists.txt -text +SAS/DataManagement/CleanupService/__init__.py -text +SAS/DataManagement/CleanupService/cleanup -text +SAS/DataManagement/CleanupService/cleanupservice -text +SAS/DataManagement/CleanupService/cleanupservice.ini -text +SAS/DataManagement/CleanupService/config.py -text +SAS/DataManagement/CleanupService/rpc.py -text +SAS/DataManagement/CleanupService/service.py -text +SAS/DataManagement/CleanupService/test/CMakeLists.txt -text +SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.py -text +SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.run -text +SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.sh -text +SAS/DataManagement/DataManagementCommon/CMakeLists.txt -text +SAS/DataManagement/DataManagementCommon/__init__.py -text +SAS/DataManagement/DataManagementCommon/config.py -text +SAS/DataManagement/DataManagementCommon/datamanagementbuslistener.py -text +SAS/DataManagement/DataManagementCommon/getPathForTask -text +SAS/DataManagement/DataManagementCommon/path.py -text +SAS/DataManagement/StorageQueryService/CMakeLists.txt -text +SAS/DataManagement/StorageQueryService/__init__.py -text +SAS/DataManagement/StorageQueryService/cache.py -text +SAS/DataManagement/StorageQueryService/config.py -text +SAS/DataManagement/StorageQueryService/diskusage.py -text +SAS/DataManagement/StorageQueryService/rpc.py -text +SAS/DataManagement/StorageQueryService/service.py -text +SAS/DataManagement/StorageQueryService/storagequery -text +SAS/DataManagement/StorageQueryService/storagequeryservice -text +SAS/DataManagement/StorageQueryService/storagequeryservice.ini -text +SAS/DataManagement/StorageQueryService/test/CMakeLists.txt -text +SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.py -text +SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.run -text +SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.sh -text SAS/Feedback_Service/CMakeLists.txt -text SAS/Feedback_Service/package.dox -text SAS/Feedback_Service/src/CMakeLists.txt -text @@ -4726,11 +4822,13 @@ SAS/MoM/CMakeLists.txt -text SAS/MoM/MoMQueryService/CMakeLists.txt -text SAS/MoM/MoMQueryService/__init__.py -text SAS/MoM/MoMQueryService/config.py -text +SAS/MoM/MoMQueryService/momcopytask -text SAS/MoM/MoMQueryService/momquery -text SAS/MoM/MoMQueryService/momqueryrpc.py -text SAS/MoM/MoMQueryService/momqueryservice -text SAS/MoM/MoMQueryService/momqueryservice.ini -text SAS/MoM/MoMQueryService/momqueryservice.py -text +SAS/MoM/MoMQueryService/momrpc.py -text SAS/MoM/MoMQueryService/test/CMakeLists.txt -text SAS/MoM/MoMQueryService/test/test_momqueryservice.py -text SAS/MoM/MoMQueryService/test/test_momqueryservice.run -text @@ -4962,6 +5060,7 @@ SAS/OTDB_Services/TreeService.py -text SAS/OTDB_Services/TreeStatusEvents.ini -text SAS/OTDB_Services/TreeStatusEvents.py -text SAS/OTDB_Services/config.py -text +SAS/OTDB_Services/otdbrpc.py -text SAS/OTDB_Services/test/CMakeLists.txt -text SAS/OTDB_Services/test/t_TreeService.py -text SAS/OTDB_Services/test/t_TreeService.run -text svneol=unset#application/x-shellscript @@ -4993,6 +5092,8 @@ SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/__init__.py -text SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator -text SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator.ini -text SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py -text +SAS/ResourceAssignment/RAScripts/CMakeLists.txt -text +SAS/ResourceAssignment/RAScripts/povero -text SAS/ResourceAssignment/RATaskSpecifiedService/bin/CMakeLists.txt -text SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice -text SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice.ini -text @@ -5007,7 +5108,6 @@ SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/bin/rotspservice.ini SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/CMakeLists.txt -text SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/__init__.py -text SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/config.py -text -SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/otdbrpc.py -text SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/propagator.py -text SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/rotspservice.py -text SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/translator.py -text @@ -5023,7 +5123,9 @@ SAS/ResourceAssignment/ResourceAssigner/lib/CMakeLists.txt -text SAS/ResourceAssignment/ResourceAssigner/lib/__init__.py -text SAS/ResourceAssignment/ResourceAssigner/lib/assignment.py -text SAS/ResourceAssignment/ResourceAssigner/lib/config.py -text +SAS/ResourceAssignment/ResourceAssigner/lib/rabuslistener.py -text SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py -text +SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py -text SAS/ResourceAssignment/ResourceAssigner/test/CMakeLists.txt -text SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py -text SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.run -text @@ -5055,15 +5157,17 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/config/__init__.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/config/default.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/__init__.py -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/fakedata.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py -text -SAS/ResourceAssignment/ResourceAssignmentEditor/lib/radbchangeshandler.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/bootstrap.min.css -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/favicon.ico -text @@ -5072,6 +5176,14 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/fonts/glyphicons-half SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/fonts/glyphicons-halflings-regular.ttf -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/fonts/glyphicons-halflings-regular.woff -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/fonts/glyphicons-halflings-regular.woff2 -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/blocked.png -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_failed.png -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_in_progress.png -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_successful.png -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.jpg -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.png -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-animate/angular-animate.min.js -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-aria/angular-aria.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/.bower.json -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angular-gantt-bounds-plugin.css -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angular-gantt-bounds-plugin.js -text @@ -5161,6 +5273,8 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angu SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angular-gantt.min.css -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angular-gantt.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-gantt/angular-gantt.min.js.map -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.css -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-moment/.bower.json -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-moment/.editorconfig -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-moment/.jshintrc -text @@ -5174,6 +5288,7 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-resource/a SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-resource/angular-resource.min.js.map -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-route/angular-route.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-route/angular-route.min.js.map -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-sanitize/angular-sanitize.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-touch/angular-touch.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-touch/angular-touch.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-touch/angular-touch.min.js.map -text @@ -5195,9 +5310,7 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-tabs/ta SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-tree/angular-ui-tree.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-tree/angular-ui-tree.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular-csp.css -text -SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js -text -SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js.map -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/highcharts/exporting.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/highcharts/highcharts-ng.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/highcharts/highcharts.js -text @@ -5215,7 +5328,9 @@ SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/datetimepick SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/startswith.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/ui-bootstrap-tpls.min.js -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/utils/ui-grid-edit-datepicker.js -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/storage.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html -text +SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/utils.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py -text SAS/ResourceAssignment/ResourceAssignmentEditor/test/CMakeLists.txt -text @@ -5229,6 +5344,7 @@ SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice -text SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice.ini -text SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/CMakeLists.txt -text SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/__init__.py -text +SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_pipeline_estimator.py -text SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_resource_estimator.py -text SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/calibration_pipeline.py -text SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/image_pipeline.py -text @@ -5255,6 +5371,11 @@ SAS/ResourceAssignment/ResourceAssignmentService/test/CMakeLists.txt -text SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.py -text SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.run -text SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.sh -text +SAS/ResourceAssignment/Services/src/rataskspecifiedservice -text +SAS/ResourceAssignment/Services/src/rataskspecifiedservice.ini -text +SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_correlator -text +SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_preprocessing -text +SAS/ResourceAssignment/Services/test/tRATaskSpecified.sh eol=lf SAS/ResourceAssignment/SystemStatusDatabase/CMakeLists.txt -text SAS/ResourceAssignment/SystemStatusDatabase/__init__.py -text SAS/ResourceAssignment/SystemStatusDatabase/sql/CMakeLists.txt -text @@ -5400,6 +5521,14 @@ SAS/XML_generator/test/test_regression.in_data/xml/test_input_cep4.xml -text SAS/XML_generator/test/test_regression.in_data/xml/test_input_long_baseline_pipeline.xml -text SAS/XML_generator/test/test_regression.py -text SAS/XML_generator/test/test_regression.sh -text +SubSystems/DataManagement/CMakeLists.txt -text +SubSystems/DataManagement/DataManagement.ini -text +SubSystems/Dragnet/scripts/LOFAR-Dragnet-activate.sh eol=lf +SubSystems/Dragnet/scripts/LOFAR-Dragnet-deploy.sh eol=lf +SubSystems/Dragnet/scripts/casacore-measures-tables/apply_casacore_measures_data.sh eol=lf +SubSystems/Dragnet/scripts/casacore-measures-tables/casacore_measures_common.sh eol=lf +SubSystems/Dragnet/scripts/casacore-measures-tables/cron-update-IERS-DRAGNET.sh eol=lf +SubSystems/Dragnet/scripts/casacore-measures-tables/get_casacore_measures_data.sh eol=lf SubSystems/LAPS_CEP/test/startPythonFromMsg.py eol=lf SubSystems/LAPS_CEP/test/startPythonFromMsg.run eol=lf SubSystems/LAPS_CEP/test/startPythonFromMsg.sh eol=lf @@ -5414,6 +5543,7 @@ SubSystems/Online_Cobalt/install/install_casacore.sh eol=lf SubSystems/Online_Cobalt/install/install_qpid.sh eol=lf SubSystems/Online_Cobalt/install/lofarsys/bash_profile -text SubSystems/Online_Cobalt/install/lofarsys/bashrc -text +SubSystems/Online_Cobalt/install/lofarsys/crontab -text SubSystems/Online_Cobalt/install/postinstall.sh eol=lf SubSystems/Online_Cobalt/install/postinstall_lofarbuild.sh eol=lf SubSystems/Online_Cobalt/install/postinstall_lofarsys.sh eol=lf @@ -5480,6 +5610,21 @@ SubSystems/Online_Cobalt/test/tgenerateStationStreams.sh eol=lf SubSystems/Online_Cobalt/test/tstartBGL.in_parset -text SubSystems/Online_Cobalt/test/tstartBGL.run eol=lf SubSystems/Online_Cobalt/test/tstartBGL.sh eol=lf +SubSystems/Online_Cobalt/validation/cep4/docker/9311-cgroupdriver.test -text +SubSystems/Online_Cobalt/validation/cep4/docker/datalocation.test -text +SubSystems/Online_Cobalt/validation/cep4/lofarsys/docker.root.test -text +SubSystems/Online_Cobalt/validation/cep4/lofarsys/ssh_localhost.root.test -text +SubSystems/Online_Cobalt/validation/cep4/network/9228-ipoib.test -text +SubSystems/Online_Cobalt/validation/cep4/os/9226-utc.test -text +SubSystems/Online_Cobalt/validation/cep4/os/9376-hostname.test -text +SubSystems/Online_Cobalt/validation/cep4/os/lustre-mounted.test -text +SubSystems/Online_Cobalt/validation/cep4/os/services.test -text +SubSystems/Online_Cobalt/validation/cep4/os/users.test -text +SubSystems/Online_Cobalt/validation/cep4/packages/iperf.test -text +SubSystems/Online_Cobalt/validation/cep4/packages/jenkins-reqs.test -text +SubSystems/Online_Cobalt/validation/cep4/qpid/9315-no_auth.test -text +SubSystems/Online_Cobalt/validation/cep4/slurm/available.test -text +SubSystems/Online_Cobalt/validation/cep4/slurm/nhc.test -text SubSystems/Online_Cobalt/validation/cluster/c3/cexec -text SubSystems/Online_Cobalt/validation/cluster/connectivity/cobalt2cep.test eol=lf SubSystems/Online_Cobalt/validation/cluster/connectivity/cobalt2cobalt.test eol=lf diff --git a/CEP/Calibration/BBSControl/scripts/parmdbplot.py b/CEP/Calibration/BBSControl/scripts/parmdbplot.py index 4b52109341672f92d653bf8ac1ec4936adc2fcd2..1d057bd4dba79b15831ea21ffebd11ff6ee9800b 100755 --- a/CEP/Calibration/BBSControl/scripts/parmdbplot.py +++ b/CEP/Calibration/BBSControl/scripts/parmdbplot.py @@ -287,9 +287,11 @@ class Parm: if self._calType == 'Clock': return QColor('#fff1bf') if self._calType == 'TEC': return QColor('#bfffda') if self._calType == 'CommonScalarPhase': return QColor('#ffbfbf') + if self._calType == 'CommonScalarAmplitude': return QColor('#ffbfbf') if self._calType == 'ScalarPhase': return QColor('#ffbf00') + if self._calType == 'ScalarAmplitude': return QColor('#ffbf00') if self._calType == 'RotationMeasure': return QColor('#84f0aa') - return QColor('#000000') + return QColor('#FFFFFF') def valueAmp(self, domain=None, resolution=None, asPolar=True): self.updateValue(domain, resolution) @@ -381,7 +383,7 @@ class Parm: def _readValue(self, domain=None, resolution=None): if self._elements is None: value = numpy.array(self.__fetch_value(self._name, domain, resolution)) - self._value = (numpy.zeros(value.shape), value) + self._value = (value, value) else: el0 = None if not self._elements[0] is None: @@ -729,39 +731,42 @@ class PlotWindow(QFrame): # Selector for double plot if self.calType == 'Gain' or self.calType == 'DirectionalGain': - double = True + plot_type = 'double' + elif self.calType == 'CommonScalarAmplitude': + plot_type = 'amp' else: self.usepointsCheck.setEnabled(False) - double = False + plot_type = 'ph' if not self.domain is None: for parm in self.selected_parms: - valuePhase = parm.valuePhase(self.domain, self.resolution, asPolar = self.polar, \ + if plot_type == 'double' or plot_type == 'ph': + valuePhase = parm.valuePhase(self.domain, self.resolution, asPolar = self.polar, \ unwrap_phase=self.unwrap_phase, reference_parm=self.reference_parm, sum_parms=self.sum_parm) - if self.axis == 0: # time on x-axis - phase.append(valuePhase[:, self.index]) - else: # freq on x-axis - phase.append(valuePhase[self.index, :]) - - if self.valuesonxaxis: - if self.axis == 0: # time on x-axis - xvalues.append((parm._times-parm._times[0])/60.) + if self.axis == 0: # time on x-axis + phase.append(valuePhase[:, self.index]) else: # freq on x-axis - xvalues.append(parm._freqs/1.e6) - else: - xvalues.append(range(len(phase[0]))) + phase.append(valuePhase[self.index, :]) - self.xminmax=[xvalues[0][0],xvalues[0][-1]] - - if double: + if plot_type == 'double' or plot_type == 'amp': valueAmp = parm.valueAmp(self.domain, self.resolution, asPolar = self.polar) if self.axis == 0: # time on x-axis amp.append(valueAmp[:, self.index]) else: # freq on x-axis amp.append(valueAmp[self.index, :]) + + if self.axis == 0: # time on x-axis + xvalues.append((parm._times-parm._times[0])/60.) + else: # freq on x-axis + xvalues.append(parm._freqs/1.e6) + + if not self.valuesonxaxis: + xvalues[-1] = range(len(xvalues[-1])) + + self.xminmax=[xvalues[0][0],xvalues[0][-1]] labels.append(parm._name) @@ -777,20 +782,24 @@ class PlotWindow(QFrame): phaselabel = "Phase (rad)" - if double: + if plot_type == 'double': # put nans to 0 [numpy.putmask(amp[i], amp[i]!=amp[i], 0) for i in xrange(len(amp))] [numpy.putmask(phase[i], phase[i]!=phase[i], 0) for i in xrange(len(phase))] if self.polar: - self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Amplitude",scatter=self.use_points) + self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Amplitude", scatter=self.use_points) self.valminmax[1] = plot(self.fig, phase, x=xvalues, clf=False, sub="212", stack=True, scatter=True, labels=labels, show_legend=legend, xlabel=xlabel, ylabel=phaselabel) else: - self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Real",scatter=self.use_points) - self.valminmax[1] = plot(self.fig, phase, x=xvalues, clf=False, sub="212", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Imaginary",scatter=self.use_points) - else: + self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Real", scatter=self.use_points) + self.valminmax[1] = plot(self.fig, phase, x=xvalues, clf=False, sub="212", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Imaginary", scatter=self.use_points) + elif plot_type == 'ph': # put nans to 0 [numpy.putmask(phase[i], phase[i]!=phase[i], 0) for i in xrange(len(phase))] self.valminmax[0] = plot(self.fig, phase, x=xvalues, sub="111", stack=True, scatter=True, labels=labels, show_legend=legend, xlabel=xlabel, ylabel=phaselabel) + elif plot_type == 'amp': + # put nans to 0 + [numpy.putmask(amp[i], amp[i]!=amp[i], 0) for i in xrange(len(amp))] + self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="111", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Amplitude", scatter=self.use_points) self.resize_plot() self.canvas.draw() diff --git a/CEP/Calibration/BBSControl/src/bbs-shared-estimator.cc b/CEP/Calibration/BBSControl/src/bbs-shared-estimator.cc index 5fd06d9b049fe629c47d61defd5fc4625abe2687..f0153f75fd34aee07bbe68fed6d423ef7b90e542 100644 --- a/CEP/Calibration/BBSControl/src/bbs-shared-estimator.cc +++ b/CEP/Calibration/BBSControl/src/bbs-shared-estimator.cc @@ -175,7 +175,7 @@ int run(const ParameterSet &options, const OptionParser::ArgumentList&) wait = false; } - pair<CommandId, shared_ptr<const Command> > command = session.getCommand(); + pair<CommandId, LOFAR::shared_ptr<Command> > command = session.getCommand(); if(command.second) { LOG_DEBUG_STR("Executing a " << command.second->type() << " command:" diff --git a/CEP/DP3/AOFlagger/src/aoquality.cpp b/CEP/DP3/AOFlagger/src/aoquality.cpp index 2e851b4b6e5707377abcdfc6b179f90ada67a56e..3fa1ca758de2bfa276fda6c625ae372a5f0c46b8 100644 --- a/CEP/DP3/AOFlagger/src/aoquality.cpp +++ b/CEP/DP3/AOFlagger/src/aoquality.cpp @@ -202,9 +202,9 @@ void actionCollect(const std::string &filename, enum CollectingMode mode, Statis { case CollectDefault: if(antennaIsFlagged || timestepIndex < flaggedTimesteps) - statisticsCollection.Add(antenna1Index, antenna2Index, time, bandIndex, p, &samples[p]->real(), &samples[p]->imag(), isRFI[p], correlatorFlagsForBadAntenna, band.channels.size() - startChannel, 2, 1, 1); + statisticsCollection.Add(antenna1Index, antenna2Index, time, bandIndex, p, &reinterpret_cast<float*>(samples[p])[0], &reinterpret_cast<float*>(samples[p])[1], isRFI[p], correlatorFlagsForBadAntenna, band.channels.size() - startChannel, 2, 1, 1); else - statisticsCollection.Add(antenna1Index, antenna2Index, time, bandIndex, p, &samples[p]->real(), &samples[p]->imag(), isRFI[p], correlatorFlags, band.channels.size() - startChannel, 2, 1, 1); + statisticsCollection.Add(antenna1Index, antenna2Index, time, bandIndex, p, &reinterpret_cast<float*>(samples[p])[0], &reinterpret_cast<float*>(samples[p])[1], isRFI[p], correlatorFlags, band.channels.size() - startChannel, 2, 1, 1); break; case CollectHistograms: histogramCollection.Add(antenna1Index, antenna2Index, p, samples[p], isRFI[p], band.channels.size() - startChannel); diff --git a/CEP/DP3/CMakeLists.txt b/CEP/DP3/CMakeLists.txt index 7c693c95b83204d161169f818b25eee11332a6b6..0a0dd7fac7585ec90ddbaed69e31e0efc04c4832 100644 --- a/CEP/DP3/CMakeLists.txt +++ b/CEP/DP3/CMakeLists.txt @@ -5,5 +5,5 @@ lofar_add_package(TestDynDPPP) lofar_add_package(PythonDPPP) lofar_add_package(DPPP_AOFlag) lofar_add_package(SPW_Combine SPWCombine) -lofar_add_package(AOFlagger) +#lofar_add_package(AOFlagger) diff --git a/CEP/DP3/DPPP/include/DPPP/ApplyBeam.h b/CEP/DP3/DPPP/include/DPPP/ApplyBeam.h index 2f80b2f860f61a03d215ebea29a2d19b3bf09cfa..c27c34e2af0ae3470e266a780f048e77184a4070 100644 --- a/CEP/DP3/DPPP/include/DPPP/ApplyBeam.h +++ b/CEP/DP3/DPPP/include/DPPP/ApplyBeam.h @@ -82,13 +82,14 @@ namespace LOFAR { template<typename T> static void applyBeam( - const DPInfo& info, double time, T* data0, + const DPInfo& info, double time, T* data0, float* weight0, const StationResponse::vector3r_t& srcdir, const StationResponse::vector3r_t& refdir, const StationResponse::vector3r_t& tiledir, const vector<StationResponse::Station::Ptr>& antBeamInfo, vector<StationResponse::matrix22c_t>& beamValues, - bool useChannelFreq, bool invert, int mode=DEFAULT); + bool useChannelFreq, bool invert, int mode, + bool doUpdateWeights=false); private: StationResponse::vector3r_t dir2Itrf( @@ -100,6 +101,7 @@ namespace LOFAR { string itsName; DPBuffer itsBuffer; bool itsInvert; + bool itsUpdateWeights; bool itsUseChannelFreq; Position itsPhaseRef; BeamMode itsMode; diff --git a/CEP/DP3/DPPP/include/DPPP/ApplyBeam.tcc b/CEP/DP3/DPPP/include/DPPP/ApplyBeam.tcc index ecaeef750c8211871841e1b32623b02c309818bf..d241f43c03c2d61c49d5f79469693901cdb86d5e 100644 --- a/CEP/DP3/DPPP/include/DPPP/ApplyBeam.tcc +++ b/CEP/DP3/DPPP/include/DPPP/ApplyBeam.tcc @@ -66,13 +66,13 @@ namespace LOFAR { // applyBeam is templated on the type of the data, could be double or float template<typename T> void ApplyBeam::applyBeam( - const DPInfo& info, double time, T* data0, + const DPInfo& info, double time, T* data0, float* weight0, const StationResponse::vector3r_t& srcdir, const StationResponse::vector3r_t& refdir, const StationResponse::vector3r_t& tiledir, const vector<StationResponse::Station::Ptr>& antBeamInfo, vector<StationResponse::matrix22c_t>& beamValues, bool useChannelFreq, - bool invert, int mode) + bool invert, int mode, bool doUpdateWeights) { // Get the beam values for each station. uint nCh = info.chanFreqs().size(); @@ -162,6 +162,10 @@ void ApplyBeam::applyBeam( data[1] = tmp[0] * r[1] + tmp[1] * r[3]; data[2] = tmp[2] * r[0] + tmp[3] * r[2]; data[3] = tmp[2] * r[1] + tmp[3] * r[3]; + + if (doUpdateWeights) { + ApplyCal::applyWeights(l, r, weight0 + bl * 4 * nCh + ch * 4); + } } } } diff --git a/CEP/DP3/DPPP/include/DPPP/ApplyCal.h b/CEP/DP3/DPPP/include/DPPP/ApplyCal.h index 19508cef2a224c1384a857601bd5bb9629de55a3..b827eaa81d45d260d05328791932e9485fd2cc1a 100644 --- a/CEP/DP3/DPPP/include/DPPP/ApplyCal.h +++ b/CEP/DP3/DPPP/include/DPPP/ApplyCal.h @@ -78,15 +78,33 @@ namespace LOFAR { // Invert a 2x2 matrix in place static void invert (casa::DComplex* v, double sigmaMMSE=0); - private: // Apply a diagonal Jones matrix to the 2x2 visibilities matrix: A.V.B^H - void applyDiag (casa::Complex* vis, float* weight, bool* flag, - uint bl, int chan, int time); + static void applyDiag (const casa::DComplex* gainA, + const casa::DComplex* gainB, + casa::Complex* vis, float* weight, bool* flag, + uint bl, uint chan, bool updateWeights, + FlagCounter& flagCounter); // Apply a full Jones matrix to the 2x2 visibilities matrix: A.V.B^H - void applyFull (casa::Complex* vis, float* weight, bool* flag, - uint bl, int chan, int time); + static void applyFull (const casa::DComplex* gainA, + const casa::DComplex* gainB, + casa::Complex* vis, float* weight, bool* flag, + uint bl, uint chan, bool updateWeights, + FlagCounter& flagCounter); + + // Do the same as the combination of BBS + python script + // covariance2weight.py (cookbook), except it stores weights per freq. + // The diagonal of covariance matrix is transferred to the weights. + // Note that the real covariance (mixing of noise terms after which they + // are not independent anymore) is not stored. + // The input covariance matrix C is assumed to be diagonal with elements + // w_i (the weights), the result the diagonal of + // (gainA kronecker gainB^H).C.(gainA kronecker gainB^H)^H + static void applyWeights (const casa::DComplex* gainA, + const casa::DComplex* gainB, + float* weight); + private: // Read parameters from the associated parmdb and store them in itsParms void updateParms (const double bufStartTime); @@ -113,7 +131,7 @@ namespace LOFAR { vector<casa::String> itsParmExprs; // parameters, numparms, antennas, time x frequency - vector<vector<vector<casa::DComplex> > > itsParms; + casa::Cube<casa::DComplex> itsParms; uint itsTimeStep; // time step within current chunk uint itsNCorr; double itsTimeInterval; diff --git a/CEP/DP3/DPPP/include/DPPP/Averager.h b/CEP/DP3/DPPP/include/DPPP/Averager.h index c9bdec28fd0d540e72d19cac42958e2645d7fe27..5b43c5ef46dee8b5e340be40ee334ca487e41cc6 100644 --- a/CEP/DP3/DPPP/include/DPPP/Averager.h +++ b/CEP/DP3/DPPP/include/DPPP/Averager.h @@ -91,6 +91,10 @@ namespace LOFAR { const casa::Cube<bool>& flags, int timeIndex); + // Get the value in Hertz of a string like "1000 MHz". If unit is + // omitted it defaults to Hertz + double getFreqHz(const string& freqstr); + //# Data members. DPInput* itsInput; string itsName; @@ -101,6 +105,8 @@ namespace LOFAR { casa::Cube<casa::Complex> itsAvgAll; casa::Cube<float> itsWeightAll; casa::Cube<bool> itsFullResFlags; + double itsFreqResolution; + double itsTimeResolution; uint itsNChanAvg; uint itsNTimeAvg; uint itsMinNPoint; diff --git a/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt b/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt index c92e64372dfee92abe459266e5f3c0cb4fc91f8a..498b197ff2707c1575eeacd8b774b201578d8949 100644 --- a/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt +++ b/CEP/DP3/DPPP/include/DPPP/CMakeLists.txt @@ -16,7 +16,8 @@ set(inst_HEADERS DemixerNew.h DemixInfo.h DemixWorker.h ApplyBeam.h ApplyBeam.tcc Predict.h - GainCal.h StefCal.h + GainCal.h StefCal.h phasefitter.h + StManParsetKeys.h ) # Create symbolic link to include directory. diff --git a/CEP/DP3/DPPP/include/DPPP/GainCal.h b/CEP/DP3/DPPP/include/DPPP/GainCal.h index c2e2344c9b0fb434b5c1fe16e1c50d1eeb5b0e21..0dd60d5c2ea58c2fdd438c3f60041cdd446800be 100644 --- a/CEP/DP3/DPPP/include/DPPP/GainCal.h +++ b/CEP/DP3/DPPP/include/DPPP/GainCal.h @@ -29,6 +29,8 @@ #include <DPPP/DPInput.h> #include <DPPP/DPBuffer.h> +#include <DPPP/phasefitter.h> +#include <DPPP/BaselineSelection.h> #include <DPPP/StefCal.h> #include <DPPP/Patch.h> #include <DPPP/Predict.h> @@ -87,36 +89,56 @@ namespace LOFAR { // Perform stefcal (polarized or unpolarized) void stefcal(); + // Apply the solution + void applySolution(DPBuffer& buf, const casa::Cube<casa::DComplex>& invsol); + + // Invert solution (for applying it) + casa::Cube<casa::DComplex> invertSol(const casa::Cube<casa::DComplex>& sol); + // Counts the number of antennas with non-flagged data, // Set a map for the used antennas in iS, returns the number of antennas - uint setAntennaMaps (const casa::Bool* flag, uint freqCell); + void setAntennaMaps (const casa::Bool* flag, uint freqCell); // Remove rows and colums corresponding to antennas with too much // flagged data from vis and mvis void removeDeadAntennas (); // Fills the matrices itsVis and itsMVis - void fillMatrices (casa::Complex* model, casa::Complex* data, float* weight, - const casa::Bool* flag); + void fillMatrices (casa::Complex* model, casa::Complex* data, + float* weight, const casa::Bool* flag); + + // Initialize the parmdb + void initParmDB(); + + // Get parmdbname from itsMode + string parmName(); + + // Write out the solutions of the current parameter chunk (timeslotsperparmupdate) + void writeSolutions (double startTime); //# Data members. DPInput* itsInput; string itsName; - DPBuffer itsBuf; + vector<DPBuffer> itsBuf; bool itsUseModelColumn; casa::Cube<casa::Complex> itsModelData; string itsParmDBName; shared_ptr<BBS::ParmDB> itsParmDB; string itsMode; - uint itsTStep; uint itsDebugLevel; bool itsDetectStalling; + bool itsApplySolution; + vector<Baseline> itsBaselines; - vector<casa::Cube<casa::DComplex> > itsSols; // for every timeslot, nSt x nCr x nFreqCells + vector<casa::Matrix<casa::DComplex> > itsPrevSol; // previous solution, for propagating solutions, for each freq + vector<casa::Cube<casa::DComplex> > itsSols; // for every timeslot, nCr x nSt x nFreqCells + vector<casa::Matrix<double> > itsTECSols; // for every timeslot, 2 x nSt (alpha and beta) + + vector<casa::CountedPtr<PhaseFitter> > itsPhaseFitters; // Length nSt std::vector<StefCal> iS; @@ -125,24 +147,34 @@ namespace LOFAR { ResultStep* itsResultStep; // For catching results from Predict or Beam bool itsApplyBeamToModelColumn; - casa::Vector<casa::String> itsAntennaUsedNames; + BaselineSelection itsBaselineSelection; // Filter + map<string,int> itsParmIdMap; //# -1 = new parm name uint itsMaxIter; double itsTolerance; - bool itsPropagateSolutions; - uint itsSolInt; - uint itsNChan; + bool itsPropagateSolutions; // Not used currently, TODO: use this + uint itsSolInt; // Time cell size + uint itsNChan; // Frequency cell size uint itsNFreqCells; uint itsMinBLperAnt; + uint itsTimeSlotsPerParmUpdate; uint itsConverged; uint itsNonconverged; + uint itsFailed; uint itsStalled; - uint itsNTimes; + vector<uint> itsNIter; // Total iterations made (for converged, stalled, nonconverged, failed) + uint itsStepInParmUpdate; // Timestep within parameter update + double itsChunkStartTime; // First time value of chunk to be stored + uint itsStepInSolInt; // Timestep within solint + + FlagCounter itsFlagCounter; + NSTimer itsTimer; NSTimer itsTimerPredict; NSTimer itsTimerSolve; + NSTimer itsTimerPhaseFit; NSTimer itsTimerWrite; NSTimer itsTimerFill; }; diff --git a/CEP/DP3/DPPP/include/DPPP/MSUpdater.h b/CEP/DP3/DPPP/include/DPPP/MSUpdater.h index ae2a6c66d1435bb53adefb8336211aecfc521caf..1ce92bdc62e62ed54be34fd014bdbf9374fab9d3 100644 --- a/CEP/DP3/DPPP/include/DPPP/MSUpdater.h +++ b/CEP/DP3/DPPP/include/DPPP/MSUpdater.h @@ -28,6 +28,8 @@ // @brief DPPP step writing to an MS #include <DPPP/DPStep.h> +#include <DPPP/StManParsetKeys.h> + #include <Common/LofarTypes.h> #include <tables/Tables/ColumnDesc.h> #include <tables/Tables/RefRows.h> @@ -122,6 +124,8 @@ namespace LOFAR { bool itsWeightColAdded; //# has weight column been added? bool itsWriteHistory; //# Should history be written? NSTimer itsTimer; + uint itsTileSize; + StManParsetKeys itsStManKeys; }; } //# end namespace diff --git a/CEP/DP3/DPPP/include/DPPP/MSWriter.h b/CEP/DP3/DPPP/include/DPPP/MSWriter.h index 2ef2f60274c0c7178e2634b4ed0cf03e45fdcb3c..bd98edff54e0e4be403720f8e014867dcd3c6196 100644 --- a/CEP/DP3/DPPP/include/DPPP/MSWriter.h +++ b/CEP/DP3/DPPP/include/DPPP/MSWriter.h @@ -29,6 +29,8 @@ #include <DPPP/DPStep.h> #include <DPPP/MSReader.h> +#include <DPPP/StManParsetKeys.h> + #include <tables/Tables/Table.h> #include <tables/Tables/ColumnDesc.h> #include <tables/Tables/ScalarColumn.h> @@ -93,7 +95,7 @@ namespace LOFAR { // Create an array column description and add to table with given // stoage manager (if given). void makeArrayColumn (casa::ColumnDesc desc, const casa::IPosition& shape, - casa::TiledColumnStMan* tsm, casa::Table& table); + casa::DataManager* dm, casa::Table& table, bool makeDirectColumn = false); // Create the MS by cloning all subtables from the input MS. // All output columns in the main table are using normal storage managers. @@ -189,6 +191,7 @@ namespace LOFAR { std::string itsVdsDir; //# directory where to put VDS file std::string itsClusterDesc; //# name of clusterdesc file NSTimer itsTimer; + StManParsetKeys itsStManKeys; }; } //# end namespace diff --git a/CEP/DP3/DPPP/include/DPPP/StManParsetKeys.h b/CEP/DP3/DPPP/include/DPPP/StManParsetKeys.h new file mode 100644 index 0000000000000000000000000000000000000000..f513e128b0779c71b6857513a6dd2b52d7863bdb --- /dev/null +++ b/CEP/DP3/DPPP/include/DPPP/StManParsetKeys.h @@ -0,0 +1,61 @@ +#ifndef DPPP_STMANPARSETKEYS_H +#define DPPP_STMANPARSETKEYS_H + +#include <Common/ParameterSet.h> + +#include <string> + +#include <casa/Containers/Record.h> + +namespace LOFAR { + namespace DPPP { + struct StManParsetKeys + { + casa::String stManName; + uint dyscoDataBitRate; //# Bits per data float, or 0 if data column is not compressed + uint dyscoWeightBitRate; //# Bits per weight float, or 0 if weight column is not compressed + std::string dyscoDistribution; //# Distribution assumed for compression; e.g. "Uniform" or "TruncatedGaussian" + double dyscoDistTruncation; //# For truncated distributions, the truncation point (e.g. 3 for 3 sigma in TruncGaus). + std::string dyscoNormalization; //# Kind of normalization; "AF", "RF" or "Row". + + void Set(const ParameterSet& parset, const std::string& prefix) + { + stManName = toLower(parset.getString(prefix+"storagemanager", string())); + if(stManName == "dysco") { + dyscoDataBitRate = parset.getInt( + prefix+"storagemanager.databitrate", 10); + dyscoWeightBitRate = parset.getInt( + prefix+"storagemanager.weightbitrate", 12); + dyscoDistribution = parset.getString( + prefix+"storagemanager.distribution", + "TruncatedGaussian"); + dyscoDistTruncation = parset.getDouble( + prefix+"storagemanager.disttruncation", 2.5); + dyscoNormalization = parset.getString( + prefix+"storagemanager.normalization", "AF"); + } + } + + casa::Record GetDyscoSpec() const + { + casa::Record dyscoSpec; + dyscoSpec.define ("distribution", dyscoDistribution); + dyscoSpec.define ("normalization", dyscoNormalization); + dyscoSpec.define ("distributionTruncation", dyscoDistTruncation); + // DPPP uses bitrate of 0 to disable compression of the data/weight column. + // However, Dysco does not allow the data or weight bitrates to be set to 0, + // so we set the values to something different. The values are not actually used. + uint dataBitRate = dyscoDataBitRate; + if(dataBitRate == 0) + dataBitRate = 16; + dyscoSpec.define ("dataBitCount", dataBitRate); + uint weightBitRate = dyscoWeightBitRate; + if(weightBitRate == 0) + weightBitRate = 16; + dyscoSpec.define ("weightBitCount", weightBitRate); + return dyscoSpec; + } + }; + } +} +#endif diff --git a/CEP/DP3/DPPP/include/DPPP/StefCal.h b/CEP/DP3/DPPP/include/DPPP/StefCal.h index f9fba37a388f280b11e2eacc47adbe91e72668db..51e1c8203f12e46600e8ce7acd5d805cf2f21a96 100644 --- a/CEP/DP3/DPPP/include/DPPP/StefCal.h +++ b/CEP/DP3/DPPP/include/DPPP/StefCal.h @@ -37,17 +37,20 @@ namespace LOFAR { class StefCal { public: - enum Status {CONVERGED=1, NOTCONVERGED=2, STALLED=3}; + enum Status {CONVERGED=1, NOTCONVERGED=2, STALLED=3, FAILED=4}; // mode can be "diagonal", "fulljones", "phaseonly", "scalarphase" StefCal(uint solInt, uint nChan, const string& mode, double tolerance, uint maxAntennas, bool detectStalling, uint debugLevel); - // Sets visibility matrices to zero, resizes them to nSt stations - void resetVis(uint nSt); + // Sets visibility matrices to zero + void resetVis(); // Initializes a new run of stefcal, resizes all internal vectors - void init(); + // If initSolutions is false, you are responsible for setting them + // before running the solver. You could set the solutions to those + // of the previous time step. + void init(bool initSolutions); // Perform sone iteration of stefcal. Returns CONVERGED, NOTCONVERGED // or STALLED @@ -56,7 +59,14 @@ namespace LOFAR { // Returns the solution. The return matrix has a length of maxAntennas, // which is zero for antennas for which no solution was computed. // The mapping is stored in the antenna map - casa::Matrix<casa::DComplex> getSolution(); + casa::Matrix<casa::DComplex> getSolution(bool setNaNs); + + double getWeight() { + return _totalWeight; + } + + // Increments the weight (only relevant for TEC-fitting) + void incrementWeight(float weight); // Returns a reference to the visibility matrix casa::Array<casa::DComplex>& getVis() { @@ -68,11 +78,8 @@ namespace LOFAR { return _mvis; } - // Returns a reference to the antenna map. This map has length - // maxAntennas, its values are the indices in the stefcal internal - // numbering, or zero when an antenna was not solved for - std::vector<int>& getAntMap() { - return _antMap; + casa::Vector<bool>& getStationFlagged() { + return _stationFlagged; } // Number of correlations in the solution (1,2 or 4) @@ -80,6 +87,14 @@ namespace LOFAR { return _savedNCr; } + // Number of correlations (1 or 4) + uint nCr() { + return _nCr; + } + + // Clear antFlagged + void clearStationFlagged(); + private: // Number of unknowns (nSt or 2*nSt, depending on _mode) uint nUn(); @@ -88,10 +103,12 @@ namespace LOFAR { Status relax(uint iter); void doStep_polarized(); - void doStep_unpolarized(bool phaseOnly); + void doStep_unpolarized(); + + double getAverageUnflaggedSolution(); uint _savedNCr; - std::vector<int> _antMap; // Length antennaNames, contains size(antennaNames)-nSt times the value -1 + casa::Vector<bool> _stationFlagged ; // Contains true for totally flagged stations casa::Array<casa::DComplex> _vis; // Visibility matrix casa::Array<casa::DComplex> _mvis; // Model visibility matrix casa::Matrix<casa::DComplex> _g; // Solution, indexed by station, correlation @@ -105,10 +122,13 @@ namespace LOFAR { uint _nUn; // number of unknowns uint _nCr; // number of correlations (1 or 4) uint _nSp; // number that is two for scalarphase, one else + uint _badIters; // number of bad iterations, for stalling detection + uint _veryBadIters; // number of iterations where solution got worse uint _solInt; // solution interval uint _nChan; // number of channels string _mode; // diagonal, scalarphase, fulljones or phaseonly double _tolerance; + double _totalWeight; bool _detectStalling; uint _debugLevel; diff --git a/CEP/DP3/DPPP/include/DPPP/phasefitter.h b/CEP/DP3/DPPP/include/DPPP/phasefitter.h new file mode 100644 index 0000000000000000000000000000000000000000..f09a2db8eb6ee017ee32b71a2a34f458ea58921c --- /dev/null +++ b/CEP/DP3/DPPP/include/DPPP/phasefitter.h @@ -0,0 +1,294 @@ +//# phasefitter.h: Class to perform phase fitting (TEC), allowing phase wraps +//# Copyright (C) 2016 +//# ASTRON (Netherlands Institute for Radio Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +//# +//# This file is part of the LOFAR software suite. +//# The LOFAR software suite is free software: you can redistribute it and/or +//# modify it under the terms of the GNU General Public License as published +//# by the Free Software Foundation, either version 3 of the License, or +//# (at your option) any later version. +//# +//# The LOFAR software suite is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License along +//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +//# +//# $Id: phasefitter.cc 21598 2012-07-16 08:07:34Z offringa $ +//# +//# @author André Offringa + +/** + * @file phasefitter.h Implements TEC model phase filter @ref PhaseFitter. + * @author André Offringa + * @date 2016-04-06 + */ + +#ifndef PHASE_FITTER_H +#define PHASE_FITTER_H + +#include <vector> +#include <cmath> +#include <cstring> + +/** + * Phase fitter that can force phase solutions over frequency onto a TEC model. + * To use: + * - Construct and set the frequencies (with @ref FrequencyData() ) and if already possible + * the weights (@ref WeightData()). + * - Perform a calibration iteration. + * - Set the phase values (@ref PhaseData()) and, if not yet done, the weights (@ref WeightData()) + * to the current phase solutions / weights of a single antenna. + * - Call @ref FitDataToTEC1Model() or @ref FitDataToTEC2Model(). + * - Read the new phase values. + * - When fitting multiple polarizations, the above steps should be done twice for each + * diagonal value of the Jones matrix. Also repeat for all antennae. + * - Continue the iteration with the new phase values. + * + * Methods with name containing TEC1 refer to a single-parameter TEC model (no delay), while + * methods with TEC2 refer to dual-parameter TEC model (TEC + delay). + */ +class PhaseFitter +{ + public: + /** + * Construct a phase fitter for the given number of channels. + * Weights are initialized to unity. The fitting accuracy is + * initialized to 1e-6. + * @param channelCount number of channels. + */ + PhaseFitter(size_t channelCount) : + _phases(channelCount, 0.0), + _frequencies(channelCount, 0.0), + _weights(channelCount, 1.0), + _fittingAccuracy(1e-6) + { } + + PhaseFitter(const PhaseFitter&); + PhaseFitter& operator=(const PhaseFitter&); + + /** + * Fits the given phase values to a TEC model and returns the parameters. This function is + * robust even when phase wrapping occurs. + * After this call, the @ref PhaseData() satisfy the TEC model. + * The TEC model has two parameters and are fitted as described in + * @ref FitTEC2ModelParameters(). + * @param alpha Found value for the alpha parameter. + * @param beta Found value for the beta parameter. + * @returns Cost of the found solution. + */ + double FitDataToTEC2Model(double& alpha, double& beta); + + /** + * Like @ref FitDataToTEC2Model(double&,double&), but without returning the parameters. + */ + void FitDataToTEC2Model() + { + double a,b; + FitDataToTEC2Model(a, b); + } + + /** + * Fits the given phase values to a TEC model using prior estimates of the + * model parameters. This method is similar to @ref FitDataToTEC2Model(), + * except that it will use the provided initial values of alpha and + * beta to speed up the solution. When the provided initial values + * are not accurate, the fit might not be accurate. + * + * @todo No fast method has been implemented -- instead it will perform a full parameter search. + * @param alpha Estimate of alpha parameter on input, found value on output. + * @param beta Estimate of beta parameter on input, found value on output. + */ + double FitDataToTEC2ModelWithInitialValues(double& alpha, double& beta) + { + return FitDataToTEC2Model(alpha, beta); + } + + /** + * Fit the data and get the best fitting parameters. The model + * used is f(nu) = alpha/nu + beta, with possible 2pi wrappings in the + * data. + * The phase data is not changed. + * The alpha parameter is linearly related to the TEC. The beta parameter + * is a constant phase offset, given in radians. + * The fitting algorithm uses a combination of brute force searching and + * binary-like searching (ternary search). + * @param alpha Will be set to the fitted value for the alpha parameter + * (value on input is not used). + * @param beta Will be set to the fitted value for the beta parameter + * (value on input is not used). + */ + void FitTEC2ModelParameters(double& alpha, double& beta) const; + + /** + * Get a pointer to the array of phase values. This array should be filled with the + * phases of solutions before calling one of the fit methods. @ref FitDataToTEC1Model() + * and ~TEC2~ sets this array to the fitted phases. + * Normally, these values should be set to std::arg(z) or atan2(z.imag(), z.real()), where + * z is a complex solution for one polarizations. All phases should correspond to the same + * polarizations, i.e., different polarizations (xx/yy/ll/rr, etc.) should be independently + * fitted. + * @returns Array of @ref Size() doubles with the phases. + */ + double* PhaseData() { return _phases.data(); } + + /** + * Get a constant pointer to the array of values. + * @returns Constant array of @ref Size() doubles with the phases. + */ + const double* PhaseData() const { return _phases.data(); } + + /** + * Get a pointer to the array of frequency values. This array should be set to the + * frequency values of the channels, such that FrequencyData()[ch] is the frequency + * corresponding to the phase value PhaseData()[ch]. The fitter will not change this + * array. + * @returns Array of @ref Size() doubles with the frequencies in Hz. + */ + double* FrequencyData() { return _frequencies.data(); } + + /** + * Constant frequency data. + * @returns Constant array of @ref Size() doubles with the frequencies in Hz. + */ + const double* FrequencyData() const { return _frequencies.data(); } + + /** + * This array should be filled with the weights + * of the channel phase solutions. If the solver supports weights during solving, this value should + * be set to the sum of weights of all visibilities that are used for the solution of + * this channel. If the solver does not support weights, it should be set to the number + * of unflagged visibilities used by the solver to generate the corresponding phase. Another + * way of saying this, is that the weights should be set to the esimated inverse variance of the phase + * solutions. + * + * The use of weights will make sure that noisy channels do not bias the result. Weighting is + * for example helpful to avoid that the few remaining samples in a badly RFI contaminated + * channels cause the fit to be inaccurate. + * + * While the weights could be different for each antenna solution, generally the weight of a channel + * is constant over the antennas. The latter implies that the weights can be set once before + * the solution starts, and only the @ref PhaseData() need to be changed within solution + * iterations. + * + * The weights are initially set to one. + * + * @returns Array of @ref Size() doubles with the weights. + */ + double* WeightData() { return _weights.data(); } + + /** + * Constant array of weights, as described above. + * @returns Constant array of @ref Size() doubles with weights. + */ + const double* WeightData() const { return _weights.data(); } + + /** + * Number of channels used for the fitter. + * @returns Number of channels. + */ + size_t Size() const { return _phases.size(); } + + /** + * Get the fitting accuracy. The fitter will stop once this accuracy is approximately reached. + * The default value is 1e-6. + * @returns Fitting accuracy. + */ + double FittingAccuracy() const { return _fittingAccuracy; } + + /** + * Change the fitting accuracy. See @ref FittingAccuracy(). + * @param newAccuracy New accuracy. + */ + void SetFittingAccuracy(double newAccuracy) { _fittingAccuracy = newAccuracy; } + + /** + * Evaluate the cost function for given TEC model parameters. The higher the + * cost, the worser the data fit the given parameters. + * @param alpha TEC parameter + * @param beta Phase offset parameter + * @returns sum of | nu_i / alpha + beta - theta_i |, and each sum term is + * phase unwrapped. + */ + double TEC2ModelCost(double alpha, double beta) const; + + /** + * Like @ref TEC2ModelFunc(), but 2-pi wrapped. + * @param nu Frequency in Hz + * @param alpha TEC parameter (in undefined units) + * @param beta Phase offset parameter (in radians) + * @returns | nu_i / alpha + beta | % 2pi + */ + static double TEC2ModelFunc(double nu, double alpha, double beta) + { + return alpha / nu + beta; + } + + double FitDataToTEC1Model(double& alpha); + + /** + * Like @ref FitDataToTEC1Model(double&,double&), but without returning the parameters. + */ + void FitDataToTEC1Model() + { + double a; + FitDataToTEC1Model(a); + } + + /** + * Like @ref TEC2ModelFunc(), but 2-pi wrapped. + * @param nu Frequency in Hz + * @param alpha TEC parameter (in undefined units) + * @param beta Phase offset parameter (in radians) + * @returns | nu_i / alpha + beta | % 2pi + */ + static double TEC2ModelFuncWrapped(double nu, double alpha, double beta) + { + return fmod(alpha / nu + beta, 2.0*M_PI); + } + + double FitDataToTEC1ModelWithInitialValues(double& alpha) + { + return FitDataToTEC1Model(alpha); + } + + void FitTEC1ModelParameters(double& alpha) const; + + /** + * Evaluate the cost function for given TEC model parameter. The higher the + * cost, the worser the data fit the given parameters. + * @param alpha TEC parameter + * @returns sum of | alpha / nu_i - theta_i |, and each sum term is + * phase unwrapped. + */ + double TEC1ModelCost(double alpha) const; + + /** + * Like @ref TEC1ModelFunc(), but 2-pi wrapped. + * @param nu Frequency in Hz + * @param alpha TEC parameter (in undefined units) + * @param beta Phase offset parameter (in radians) + * @returns | alpha / nu_i + beta | % 2pi + */ + static double TEC1ModelFuncWrapped(double nu, double alpha) + { + return fmod(alpha / nu, 2.0*M_PI); + } + private: + std::vector<double> _phases, _frequencies, _weights; + double _fittingAccuracy; + + double fitTEC2ModelBeta(double alpha, double betaEstimate) const; + void bruteForceSearchTEC2Model(double& lowerAlpha, double& upperAlpha, double& beta) const; + double ternarySearchTEC2ModelAlpha(double startAlpha, double endAlpha, double& beta) const; + void fillDataWithTEC2Model(double alpha, double beta); + void fillDataWithTEC1Model(double alpha); + + void bruteForceSearchTEC1Model(double& lowerAlpha, double& upperAlpha) const; + double ternarySearchTEC1ModelAlpha(double startAlpha, double endAlpha) const; +}; + +#endif diff --git a/CEP/DP3/DPPP/src/ApplyBeam.cc b/CEP/DP3/DPPP/src/ApplyBeam.cc index 7f9b139c69c384462c538383d009984652af69ea..8d1484bb1a096196e55fb6fccbe87ae0828ea1d2 100644 --- a/CEP/DP3/DPPP/src/ApplyBeam.cc +++ b/CEP/DP3/DPPP/src/ApplyBeam.cc @@ -54,10 +54,11 @@ namespace LOFAR { namespace DPPP { ApplyBeam::ApplyBeam(DPInput* input, const ParameterSet& parset, - const string& prefix, bool substep) + const string& prefix, bool substep) : itsInput(input), itsName(prefix), + itsUpdateWeights(parset.getBool(prefix + "updateweights", false)), itsUseChannelFreq(parset.getBool(prefix + "usechannelfreq", true)), itsDebugLevel(parset.getInt(prefix + "debuglevel", 0)) { @@ -94,6 +95,9 @@ namespace LOFAR { info() = infoIn; info().setNeedVisData(); info().setWriteData(); + if (itsUpdateWeights) { + info().setWriteWeights(); + } MDirection dirJ2000( MDirection::Convert(infoIn.phaseCenter(), MDirection::J2000)()); @@ -136,6 +140,7 @@ namespace LOFAR { os << endl; os << " use channelfreq: " << boolalpha << itsUseChannelFreq << endl; os << " invert: " << boolalpha << itsInvert << endl; + os << " update weights: " << boolalpha << itsUpdateWeights << endl; } void ApplyBeam::showTimings(std::ostream& os, double duration) const @@ -151,6 +156,11 @@ namespace LOFAR { itsBuffer.copy (bufin); Complex* data=itsBuffer.getData().data(); + if (itsUpdateWeights) { + itsInput->fetchWeights (bufin, itsBuffer, itsTimer); + } + float* weight = itsBuffer.getWeights().data(); + double time = itsBuffer.getTime(); //Set up directions for beam evaluation @@ -168,9 +178,9 @@ namespace LOFAR { uint thread = OpenMP::threadNum(); StationResponse::vector3r_t srcdir = refdir; - applyBeam(info(), time, data, srcdir, refdir, tiledir, + applyBeam(info(), time, data, weight, srcdir, refdir, tiledir, itsAntBeamInfo[thread], itsBeamValues[thread], - itsUseChannelFreq, itsInvert, itsMode); + itsUseChannelFreq, itsInvert, itsMode, itsUpdateWeights); itsTimer.stop(); getNextStep()->process(itsBuffer); diff --git a/CEP/DP3/DPPP/src/ApplyCal.cc b/CEP/DP3/DPPP/src/ApplyCal.cc index e161f96e9f161b590d11432b7754d22dbfe4d10d..ecbe7168017de3565e162096189189926d9fe0bf 100644 --- a/CEP/DP3/DPPP/src/ApplyCal.cc +++ b/CEP/DP3/DPPP/src/ApplyCal.cc @@ -70,6 +70,15 @@ namespace LOFAR { if (itsCorrectType=="fulljones" && itsUpdateWeights) { ASSERTSTR (itsInvert, "Updating weights has not been implemented for invert=false and fulljones"); } + ASSERT(itsCorrectType=="gain" || itsCorrectType=="fulljones" || + itsCorrectType=="tec" || itsCorrectType=="clock" || + itsCorrectType=="scalarphase" || itsCorrectType=="commonscalarphase" || + itsCorrectType=="scalaramplitude" || itsCorrectType=="commonscalaramplitude" || + itsCorrectType=="rotationangle" || itsCorrectType=="commonrotationangle" || + itsCorrectType=="rotationmeasure"); + if (itsCorrectType.substr(0,6)=="common") { + itsCorrectType=itsCorrectType.substr(6); + } } ApplyCal::ApplyCal() @@ -172,12 +181,14 @@ namespace LOFAR { itsParmExprs.push_back("Clock:0"); itsParmExprs.push_back("Clock:1"); } - } else if (itsCorrectType == "commonrotationangle") { - itsParmExprs.push_back("CommonRotationAngle"); - } else if (itsCorrectType == "commonscalarphase") { - itsParmExprs.push_back("CommonScalarPhase"); + } else if (itsCorrectType == "rotationangle") { + itsParmExprs.push_back("{Common,}RotationAngle"); + } else if (itsCorrectType == "scalarphase") { + itsParmExprs.push_back("{Common,}ScalarPhase"); } else if (itsCorrectType == "rotationmeasure") { itsParmExprs.push_back("RotationMeasure"); + } else if (itsCorrectType == "scalaramplitude") { + itsParmExprs.push_back("{Common,}ScalarAmplitude"); } else { THROW (Exception, "Correction type " + itsCorrectType + @@ -255,17 +266,24 @@ namespace LOFAR { #pragma omp parallel for for (size_t bl=0; bl<nbl; ++bl) { for (size_t chan=0;chan<nchan;chan++) { - if (itsParms.size()>2) { - applyFull( &data[bl * itsNCorr * nchan + chan * itsNCorr ], - &weight[bl * itsNCorr * nchan + chan * itsNCorr ], - &flag[ bl * itsNCorr * nchan + chan * itsNCorr ], - bl, chan, itsTimeStep); + uint timeFreqOffset=(itsTimeStep*info().nchan())+chan; + uint antA = info().getAnt1()[bl]; + uint antB = info().getAnt2()[bl]; + if (itsParms.shape()[0]>2) { + applyFull( &itsParms(0, antA, timeFreqOffset), + &itsParms(0, antB, timeFreqOffset), + &data[bl * itsNCorr * nchan + chan * itsNCorr ], + &weight[bl * itsNCorr * nchan + chan * itsNCorr ], + &flag[ bl * itsNCorr * nchan + chan * itsNCorr ], + bl, chan, itsUpdateWeights, itsFlagCounter); } else { - applyDiag( &data[bl * itsNCorr * nchan + chan * itsNCorr ], - &weight[bl * itsNCorr * nchan + chan * itsNCorr ], - &flag[ bl * itsNCorr * nchan + chan * itsNCorr ], - bl, chan, itsTimeStep); + applyDiag( &itsParms(0, antA, timeFreqOffset), + &itsParms(0, antB, timeFreqOffset), + &data[bl * itsNCorr * nchan + chan * itsNCorr ], + &weight[bl * itsNCorr * nchan + chan * itsNCorr ], + &flag[ bl * itsNCorr * nchan + chan * itsNCorr ], + bl, chan, itsUpdateWeights, itsFlagCounter); } } } @@ -289,7 +307,7 @@ namespace LOFAR { int numAnts = info().antennaNames().size(); // itsParms contains the parameters to a grid, first for all parameters - // (e.g. Gain:0:0 and Gain:1:1), next all antennas, next over frec * time + // (e.g. Gain:0:0 and Gain:1:1), next all antennas, next over freq * time // as returned by ParmDB vector<vector<vector<double> > > parmvalues; parmvalues.resize(itsParmExprs.size()); @@ -321,8 +339,16 @@ namespace LOFAR { for (uint parmExprNum = 0; parmExprNum<itsParmExprs.size();++parmExprNum) { // parmMap contains parameter values for all antennas parmMap = itsParmDB->getValuesMap( itsParmExprs[parmExprNum] + "*", - minFreq, maxFreq, freqInterval, - bufStartTime, itsLastTime, itsTimeInterval, true); + minFreq, maxFreq, freqInterval, + bufStartTime, itsLastTime, itsTimeInterval, + true); + + // Resolve {Common,}Bla to CommonBla or Bla + if (!parmMap.empty() && + itsParmExprs[parmExprNum].find("{") != std::string::npos) { + uint colonPos = (parmMap.begin()->first).find(":"); + itsParmExprs[parmExprNum] = (parmMap.begin()->first).substr(0, colonPos); + } for (int ant = 0; ant < numAnts; ++ant) { parmIt = parmMap.find( @@ -375,73 +401,73 @@ namespace LOFAR { if (itsCorrectType=="gain") { if (itsUseAP) { // Data as Amplitude / Phase - itsParms[0][ant][tf] = polar(parmvalues[0][ant][tf], - parmvalues[1][ant][tf]); - itsParms[1][ant][tf] = polar(parmvalues[2][ant][tf], - parmvalues[3][ant][tf]); + itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf], + parmvalues[1][ant][tf]); + itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf], + parmvalues[3][ant][tf]); } else { // Data as Real / Imaginary - itsParms[0][ant][tf] = DComplex(parmvalues[0][ant][tf], - parmvalues[1][ant][tf]); - itsParms[1][ant][tf] = DComplex(parmvalues[2][ant][tf], - parmvalues[3][ant][tf]); + itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf], + parmvalues[1][ant][tf]); + itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf], + parmvalues[3][ant][tf]); } } else if (itsCorrectType=="fulljones") { if (itsUseAP) { // Data as Amplitude / Phase - itsParms[0][ant][tf] = polar(parmvalues[0][ant][tf], - parmvalues[1][ant][tf]); - itsParms[1][ant][tf] = polar(parmvalues[2][ant][tf], - parmvalues[3][ant][tf]); - itsParms[2][ant][tf] = polar(parmvalues[4][ant][tf], - parmvalues[5][ant][tf]); - itsParms[3][ant][tf] = polar(parmvalues[6][ant][tf], - parmvalues[7][ant][tf]); + itsParms(0, ant, tf) = polar(parmvalues[0][ant][tf], + parmvalues[1][ant][tf]); + itsParms(1, ant, tf) = polar(parmvalues[2][ant][tf], + parmvalues[3][ant][tf]); + itsParms(2, ant, tf) = polar(parmvalues[4][ant][tf], + parmvalues[5][ant][tf]); + itsParms(3, ant, tf) = polar(parmvalues[6][ant][tf], + parmvalues[7][ant][tf]); } else { // Data as Real / Imaginary - itsParms[0][ant][tf] = DComplex(parmvalues[0][ant][tf], - parmvalues[1][ant][tf]); - itsParms[1][ant][tf] = DComplex(parmvalues[2][ant][tf], - parmvalues[3][ant][tf]); - itsParms[2][ant][tf] = DComplex(parmvalues[4][ant][tf], - parmvalues[5][ant][tf]); - itsParms[3][ant][tf] = DComplex(parmvalues[6][ant][tf], - parmvalues[7][ant][tf]); + itsParms(0, ant, tf) = DComplex(parmvalues[0][ant][tf], + parmvalues[1][ant][tf]); + itsParms(1, ant, tf) = DComplex(parmvalues[2][ant][tf], + parmvalues[3][ant][tf]); + itsParms(2, ant, tf) = DComplex(parmvalues[4][ant][tf], + parmvalues[5][ant][tf]); + itsParms(3, ant, tf) = DComplex(parmvalues[6][ant][tf], + parmvalues[7][ant][tf]); } } else if (itsCorrectType=="tec") { - itsParms[0][ant][tf]=polar(1., + itsParms(0, ant, tf)=polar(1., parmvalues[0][ant][tf] * -8.44797245e9 / freq); if (itsParmExprs.size() == 1) { // No TEC:0, only TEC: - itsParms[1][ant][tf]=polar(1., + itsParms(1, ant, tf)=polar(1., parmvalues[0][ant][tf] * -8.44797245e9 / freq); } else { // TEC:0 and TEC:1 - itsParms[1][ant][tf]=polar(1., + itsParms(1, ant, tf)=polar(1., parmvalues[1][ant][tf] * -8.44797245e9 / freq); } } else if (itsCorrectType=="clock") { - itsParms[0][ant][tf]=polar(1., + itsParms(0, ant, tf)=polar(1., parmvalues[0][ant][tf] * freq * casa::C::_2pi); if (itsParmExprs.size() == 1) { // No Clock:0, only Clock: - itsParms[1][ant][tf]=polar(1., + itsParms(1, ant, tf)=polar(1., parmvalues[0][ant][tf] * freq * casa::C::_2pi); } else { // Clock:0 and Clock:1 - itsParms[1][ant][tf]=polar(1., + itsParms(1, ant, tf)=polar(1., parmvalues[1][ant][tf] * freq * casa::C::_2pi); } } - else if (itsCorrectType=="commonrotationangle") { + else if (itsCorrectType=="rotationangle") { double phi=parmvalues[0][ant][tf]; if (itsInvert) { phi = -phi; } double sinv=sin(phi); double cosv=cos(phi); - itsParms[0][ant][tf] = cosv; - itsParms[1][ant][tf] = -sinv; - itsParms[2][ant][tf] = sinv; - itsParms[3][ant][tf] = cosv; + itsParms(0, ant, tf) = cosv; + itsParms(1, ant, tf) = -sinv; + itsParms(2, ant, tf) = sinv; + itsParms(3, ant, tf) = cosv; } else if (itsCorrectType=="rotationmeasure") { double lambda2 = casa::C::c / freq; @@ -452,21 +478,27 @@ namespace LOFAR { } double sinv = sin(chi); double cosv = cos(chi); - itsParms[0][ant][tf] = cosv; - itsParms[1][ant][tf] = -sinv; - itsParms[2][ant][tf] = sinv; - itsParms[3][ant][tf] = cosv; + itsParms(0, ant, tf) = cosv; + itsParms(1, ant, tf) = -sinv; + itsParms(2, ant, tf) = sinv; + itsParms(3, ant, tf) = cosv; + } + else if (itsCorrectType=="scalarphase") { + itsParms(0, ant, tf) = polar(1., parmvalues[0][ant][tf]); + itsParms(1, ant, tf) = polar(1., parmvalues[0][ant][tf]); } - else if (itsCorrectType=="commonscalarphase") { - itsParms[0][ant][tf] = polar(1., parmvalues[0][ant][tf]); - itsParms[1][ant][tf] = polar(1., parmvalues[0][ant][tf]); + else if (itsCorrectType=="scalaramplitude") { + itsParms(0, ant, tf) = parmvalues[0][ant][tf]; + itsParms(1, ant, tf) = parmvalues[0][ant][tf]; } - // Invert diagonal corrections (not fulljones and commonrotationangle) - // For fulljones it will be handled in applyFull - if (itsInvert && itsParms.size()==2) { - itsParms[0][ant][tf] = 1./itsParms[0][ant][tf]; - itsParms[1][ant][tf] = 1./itsParms[1][ant][tf]; + // Invert (rotationmeasure and commonrotationangle are already inverted) + if (itsInvert && itsParms.shape()[0]==2) { + itsParms(0, ant, tf) = 1./itsParms(0, ant, tf); + itsParms(1, ant, tf) = 1./itsParms(1, ant, tf); + } + else if (itsInvert && itsCorrectType=="fulljones") { + invert(&itsParms(0, ant, tf),itsSigmaMMSE); } } } @@ -478,7 +510,7 @@ namespace LOFAR { uint numParms; if (itsCorrectType=="fulljones" || - itsCorrectType=="commonrotationangle" || + itsCorrectType=="rotationangle" || itsCorrectType=="rotationmeasure") { numParms = 4; } @@ -486,54 +518,39 @@ namespace LOFAR { numParms = 2; } - itsParms.resize(numParms); - - for (uint parmNum=0;parmNum<numParms;parmNum++) { - itsParms[parmNum].resize(numAnts); - for (uint ant=0;ant<numAnts;++ant) { - itsParms[parmNum][ant].resize(tfDomainSize); - } - } + itsParms.resize(numParms, numAnts, tfDomainSize); } - void ApplyCal::applyDiag (Complex* vis, float* weight, bool* flag, - uint bl, int chan, int time) { - int timeFreqOffset=(time*info().nchan())+chan; - - uint antA = info().getAnt1()[bl]; - uint antB = info().getAnt2()[bl]; - - DComplex diag0A = itsParms[0][antA][timeFreqOffset]; - DComplex diag1A = itsParms[1][antA][timeFreqOffset]; - DComplex diag0B = itsParms[0][antB][timeFreqOffset]; - DComplex diag1B = itsParms[1][antB][timeFreqOffset]; - + void ApplyCal::applyDiag (const DComplex* gainA, const DComplex* gainB, + Complex* vis, float* weight, bool* flag, + uint bl, uint chan, bool updateWeights, + FlagCounter& flagCounter) { // If parameter is NaN or inf, do not apply anything and flag the data - if (! (isFinite(diag0A.real()) && isFinite(diag0A.imag()) && - isFinite(diag0B.real()) && isFinite(diag0B.imag()) && - isFinite(diag1A.real()) && isFinite(diag1A.imag()) && - isFinite(diag1B.real()) && isFinite(diag1B.imag())) ) { + if (! (isFinite(gainA[0].real()) && isFinite(gainA[0].imag()) && + isFinite(gainB[0].real()) && isFinite(gainB[0].imag()) && + isFinite(gainA[1].real()) && isFinite(gainA[1].imag()) && + isFinite(gainB[1].real()) && isFinite(gainB[1].imag())) ) { // Only update flagcounter for first correlation if (!flag[0]) { - itsFlagCounter.incrChannel(chan); - itsFlagCounter.incrBaseline(bl); + flagCounter.incrChannel(chan); + flagCounter.incrBaseline(bl); } - for (uint corr=0; corr<itsNCorr; ++corr) { + for (uint corr=0; corr<4; ++corr) { flag[corr]=true; } return; } - vis[0] *= diag0A * conj(diag0B); - vis[1] *= diag0A * conj(diag1B); - vis[2] *= diag1A * conj(diag0B); - vis[3] *= diag1A * conj(diag1B); + vis[0] *= gainA[0] * conj(gainB[0]); + vis[1] *= gainA[0] * conj(gainB[1]); + vis[2] *= gainA[1] * conj(gainB[0]); + vis[3] *= gainA[1] * conj(gainB[1]); - if (itsUpdateWeights) { - weight[0] /= norm(diag0A) * norm(diag0B); - weight[1] /= norm(diag0A) * norm(diag1B); - weight[2] /= norm(diag1A) * norm(diag0B); - weight[3] /= norm(diag1A) * norm(diag1B); + if (updateWeights) { + weight[0] /= norm(gainA[0]) * norm(gainB[0]); + weight[1] /= norm(gainA[0]) * norm(gainB[1]); + weight[2] /= norm(gainA[1]) * norm(gainB[0]); + weight[3] /= norm(gainA[1]) * norm(gainB[1]); } } @@ -552,34 +569,15 @@ namespace LOFAR { v[3] = v0 * invDet; } - void ApplyCal::applyFull (Complex* vis, float* weight, bool* flag, - uint bl, int chan, int time) { - int timeFreqOffset=(time*info().nchan())+chan; - DComplex gainA[4]; - DComplex gainB[4]; - - uint antA = info().getAnt1()[bl]; - uint antB = info().getAnt2()[bl]; - - gainA[0] = itsParms[0][antA][timeFreqOffset]; - gainA[1] = itsParms[1][antA][timeFreqOffset]; - gainA[2] = itsParms[2][antA][timeFreqOffset]; - gainA[3] = itsParms[3][antA][timeFreqOffset]; - - gainB[0] = itsParms[0][antB][timeFreqOffset]; - gainB[1] = itsParms[1][antB][timeFreqOffset]; - gainB[2] = itsParms[2][antB][timeFreqOffset]; - gainB[3] = itsParms[3][antB][timeFreqOffset]; - + void ApplyCal::applyFull (const DComplex* gainA, const DComplex* gainB, + Complex* vis, float* weight, bool* flag, + uint bl, uint chan, bool doUpdateWeights, + FlagCounter& flagCounter) { DComplex gainAxvis[4]; - if (itsInvert && itsCorrectType=="fulljones") { - invert(gainA,itsSigmaMMSE); - invert(gainB,itsSigmaMMSE); - } // If parameter is NaN or inf, do not apply anything and flag the data bool anyinfnan = false; - for (uint corr=0; corr<itsNCorr; ++corr) { + for (uint corr=0; corr<4; ++corr) { if (! (isFinite(gainA[corr].real()) && isFinite(gainA[corr].imag()) && isFinite(gainB[corr].real()) && isFinite(gainB[corr].imag())) ) { anyinfnan = true; @@ -589,10 +587,10 @@ namespace LOFAR { if (anyinfnan) { // Only update flag counter for first correlation if (!flag[0]) { - itsFlagCounter.incrChannel(chan); - itsFlagCounter.incrBaseline(bl); + flagCounter.incrChannel(chan); + flagCounter.incrBaseline(bl); } - for (uint corr=0; corr<itsNCorr; ++corr) { + for (uint corr=0; corr<4; ++corr) { flag[corr]=true; } return; @@ -614,47 +612,44 @@ namespace LOFAR { } } - // The code below does the same as the combination of BBS + python script - // covariance2weight.py (cookbook), except it stores weights per freq. - // The diagonal of covariance matrix is transferred to the weights. - // Note that the real covariance (mixing of noise terms after which they - // are not independent anymore) is not stored. - // The input covariance matrix C is assumed to be diagonal with elements - // w_i (the weights), the result the diagonal of - // (gainA kronecker gainB^H).C.(gainA kronecker gainB^H)^H - if (itsUpdateWeights) { - ASSERTSTR (itsInvert, "Updating weights has not been implemented for invert=false"); - float cov[4], normGainA[4], normGainB[4]; - for (uint i=0;i<4;++i) { - cov[i]=1./weight[i]; - normGainA[i]=norm(gainA[i]); - normGainB[i]=norm(gainB[i]); - } + if (doUpdateWeights) { + applyWeights(gainA, gainB, weight); + } + } - weight[0]=cov[0]*(normGainA[0]*normGainB[0]) - +cov[1]*(normGainA[0]*normGainB[1]) - +cov[2]*(normGainA[1]*normGainB[0]) - +cov[3]*(normGainA[1]*normGainB[1]); - weight[0]=1./weight[0]; - - weight[1]=cov[0]*(normGainA[0]*normGainB[2]) - +cov[1]*(normGainA[0]*normGainB[3]) - +cov[2]*(normGainA[1]*normGainB[2]) - +cov[3]*(normGainA[1]*normGainB[3]); - weight[1]=1./weight[1]; - - weight[2]=cov[0]*(normGainA[2]*normGainB[0]) - +cov[1]*(normGainA[2]*normGainB[1]) - +cov[2]*(normGainA[3]*normGainB[0]) - +cov[3]*(normGainA[3]*normGainB[1]); - weight[2]=1./weight[2]; - - weight[3]=cov[0]*(normGainA[2]*normGainB[2]) - +cov[1]*(normGainA[2]*normGainB[3]) - +cov[2]*(normGainA[3]*normGainB[2]) - +cov[3]*(normGainA[3]*normGainB[3]); - weight[3]=1./weight[3]; + void ApplyCal::applyWeights(const DComplex* gainA, + const DComplex* gainB, + float* weight) { + float cov[4], normGainA[4], normGainB[4]; + for (uint i=0;i<4;++i) { + cov[i]=1./weight[i]; + normGainA[i]=norm(gainA[i]); + normGainB[i]=norm(gainB[i]); } + + weight[0]=cov[0]*(normGainA[0]*normGainB[0]) + +cov[1]*(normGainA[0]*normGainB[1]) + +cov[2]*(normGainA[1]*normGainB[0]) + +cov[3]*(normGainA[1]*normGainB[1]); + weight[0]=1./weight[0]; + + weight[1]=cov[0]*(normGainA[0]*normGainB[2]) + +cov[1]*(normGainA[0]*normGainB[3]) + +cov[2]*(normGainA[1]*normGainB[2]) + +cov[3]*(normGainA[1]*normGainB[3]); + weight[1]=1./weight[1]; + + weight[2]=cov[0]*(normGainA[2]*normGainB[0]) + +cov[1]*(normGainA[2]*normGainB[1]) + +cov[2]*(normGainA[3]*normGainB[0]) + +cov[3]*(normGainA[3]*normGainB[1]); + weight[2]=1./weight[2]; + + weight[3]=cov[0]*(normGainA[2]*normGainB[2]) + +cov[1]*(normGainA[2]*normGainB[3]) + +cov[2]*(normGainA[3]*normGainB[2]) + +cov[3]*(normGainA[3]*normGainB[3]); + weight[3]=1./weight[3]; } void ApplyCal::showCounts (std::ostream& os) const diff --git a/CEP/DP3/DPPP/src/Averager.cc b/CEP/DP3/DPPP/src/Averager.cc index f761308d6ffeb683ca9931de1256b8bba3b0522e..b1f739fa3b6e12af0c3477a967d3865065ab7b4e 100644 --- a/CEP/DP3/DPPP/src/Averager.cc +++ b/CEP/DP3/DPPP/src/Averager.cc @@ -28,6 +28,8 @@ #include <Common/ParameterSet.h> #include <Common/LofarLogger.h> #include <casa/Arrays/ArrayMath.h> +#include <Common/StringUtil.h> + #include <iostream> #include <iomanip> @@ -41,22 +43,35 @@ namespace LOFAR { const string& prefix) : itsInput (input), itsName (prefix), - itsNChanAvg (parset.getUint (prefix+"freqstep", 1)), - itsNTimeAvg (parset.getUint (prefix+"timestep", 1)), itsMinNPoint (parset.getUint (prefix+"minpoints", 1)), itsMinPerc (parset.getFloat (prefix+"minperc", 0.) / 100.), itsNTimes (0), - itsTimeInterval (0) + itsTimeInterval (0), + itsNoAvg (true) { - if (itsNChanAvg <= 0) itsNChanAvg = 1; - if (itsNTimeAvg <= 0) itsNTimeAvg = 1; - itsNoAvg = (itsNChanAvg == 1 && itsNTimeAvg == 1); + string freqResolutionStr = parset.getString(prefix+"freqresolution","0"); + itsFreqResolution = getFreqHz(freqResolutionStr); + + if (itsFreqResolution > 0) { + itsNChanAvg = 0; // Will be set later in updateinfo + } else { + itsNChanAvg = parset.getUint (prefix+"freqstep", 1); + } + + itsTimeResolution = parset.getFloat(prefix+"timeresolution", 0.); + if (itsTimeResolution > 0) { + itsNTimeAvg = 0; // Will be set later in updateInfo + } else { + itsNTimeAvg = parset.getUint(prefix+"timestep", 1); + } } Averager::Averager (DPInput* input, const string& stepName, uint nchanAvg, uint ntimeAvg) : itsInput (input), itsName (stepName), + itsFreqResolution (0), + itsTimeResolution (0), itsNChanAvg (nchanAvg), itsNTimeAvg (ntimeAvg), itsMinNPoint (1), @@ -79,7 +94,27 @@ namespace LOFAR { info().setWriteData(); info().setWriteFlags(); info().setMetaChanged(); + + if (itsNChanAvg <= 0) { + if (itsFreqResolution > 0) { + double chanwidth = infoIn.chanWidths()[0]; + itsNChanAvg = std::max(1, (int)(itsFreqResolution / chanwidth + 0.5)); + } else { + itsNChanAvg = 1; + } + } + itsTimeInterval = infoIn.timeInterval(); + if (itsNTimeAvg <= 0) { + if (itsTimeResolution > 0) { + itsNTimeAvg = std::max(1, (int)(itsTimeResolution / itsTimeInterval + 0.5)); + } else { + itsNTimeAvg = 1; + } + } + + itsNoAvg = (itsNChanAvg == 1 && itsNTimeAvg == 1); + // Adapt averaging to available nr of channels and times. itsNTimeAvg = std::min (itsNTimeAvg, infoIn.ntime()); itsNChanAvg = info().update (itsNChanAvg, itsNTimeAvg); @@ -88,8 +123,15 @@ namespace LOFAR { void Averager::show (std::ostream& os) const { os << "Averager " << itsName << std::endl; - os << " freqstep: " << itsNChanAvg << std::endl; - os << " timestep: " << itsNTimeAvg << std::endl; + os << " freqstep: " << itsNChanAvg; + if (itsFreqResolution>0) { + os << " (set by freqresolution: " << itsFreqResolution << " Hz)" << std::endl; + } + os << " timestep: " << itsNTimeAvg; + if (itsTimeResolution>0) { + os << " (set by timeresolution: " << itsTimeResolution << ")"; + } + os << std::endl; os << " minpoints: " << itsMinNPoint << std::endl; os << " minperc: " << 100*itsMinPerc << std::endl; } @@ -339,5 +381,28 @@ namespace LOFAR { } } + double Averager::getFreqHz(const string& freqstr) { + String unit; + // See if a unit is given at the end. + String v(freqstr); + // Remove possible trailing blanks. + rtrim(v); + Regex regex("[a-zA-Z]+$"); + string::size_type pos = v.index (regex); + if (pos != String::npos) { + unit = v.from (pos); + v = v.before (pos); + } + // Set value and unit. + + double value = strToDouble(v); + if (unit.empty()) { + return value; + } else { + Quantity q(value, unit); + return q.getValue("Hz", true); + } + } + } //# end namespace } diff --git a/CEP/DP3/DPPP/src/BaselineSelection.cc b/CEP/DP3/DPPP/src/BaselineSelection.cc index fd6fcb4d25bdf35b0ee71c9ef3bd50d6bc2d74ac..1a98c9c33dad5de13152b0a7886494832dce7a0e 100644 --- a/CEP/DP3/DPPP/src/BaselineSelection.cc +++ b/CEP/DP3/DPPP/src/BaselineSelection.cc @@ -28,6 +28,7 @@ #include <Common/ParameterSet.h> #include <Common/ParameterValue.h> #include <Common/LofarLogger.h> +#include <Common/StringUtil.h> #include <Common/StreamUtil.h> using namespace casa; @@ -42,7 +43,7 @@ namespace LOFAR { BaselineSelection::BaselineSelection (const ParameterSet& parset, const string& prefix, bool minmax, - const string& defaultCorrType, + const string& defaultCorrType, const string& defaultBaseline) : itsStrBL (parset.getString (prefix + "baseline", defaultBaseline)), itsCorrType (parset.getString (prefix + "corrtype", defaultCorrType)), @@ -138,7 +139,15 @@ namespace LOFAR { // Specified in casacore's MSSelection format. string msName = info.msName(); ASSERT (! msName.empty()); - Matrix<bool> sel(BaselineSelect::convert (msName, itsStrBL)); + std::ostringstream os; + Matrix<bool> sel(BaselineSelect::convert (msName, itsStrBL, os)); + // Show possible messages about unknown stations. + if (! os.str().empty()) { + vector<string> messages = StringUtil::split (os.str(), '\n'); + for (size_t i=0; i<messages.size(); ++i) { + DPLOG_WARN_STR (messages[i]); + } + } // The resulting matrix can be smaller because new stations might have // been added that are not present in the MS's ANTENNA table. if (sel.nrow() == selectBL.nrow()) { diff --git a/CEP/DP3/DPPP/src/CMakeLists.txt b/CEP/DP3/DPPP/src/CMakeLists.txt index 51598d1c8b1ca4c08baace0914976cdcf2c3b0fa..0c4077b777b89e98dd117dad04f4b1d91a66e3b1 100644 --- a/CEP/DP3/DPPP/src/CMakeLists.txt +++ b/CEP/DP3/DPPP/src/CMakeLists.txt @@ -20,6 +20,7 @@ lofar_add_library(dppp DemixerNew.cc DemixInfo.cc DemixWorker.cc Predict.cc ApplyBeam.cc + phasefitter.cc ) lofar_add_bin_program(NDPPP NDPPP.cc) diff --git a/CEP/DP3/DPPP/src/DPRun.cc b/CEP/DP3/DPPP/src/DPRun.cc index 599a298c40bca44fb0e496995ceb9b6b0238f48f..1babeee7186cc34d0743226b1fee783daf46c7d5 100644 --- a/CEP/DP3/DPPP/src/DPRun.cc +++ b/CEP/DP3/DPPP/src/DPRun.cc @@ -103,8 +103,8 @@ namespace LOFAR { NSTimer nstimer; nstimer.start(); ParameterSet parset; - if (parsetName!="") { - parset.adoptFile(parsetName); + if (! parsetName.empty()) { + parset.adoptFile (parsetName); } // Adopt possible parameters given at the command line. parset.adoptArgv (argc, argv); //# works fine if argc==0 and argv==0 diff --git a/CEP/DP3/DPPP/src/FlagCounter.cc b/CEP/DP3/DPPP/src/FlagCounter.cc index edd1e3661870b965025b5e31290a1acc231fcaa7..5b96edf8ee677fa42ce10854e6a305969d45b2ca 100644 --- a/CEP/DP3/DPPP/src/FlagCounter.cc +++ b/CEP/DP3/DPPP/src/FlagCounter.cc @@ -265,6 +265,10 @@ namespace LOFAR { os << endl; } int64 totalnpoints = npoints * itsChanCounts.size(); + // Prevent division by zero + if (totalnpoints == 0) { + totalnpoints = 1; + } os << "Total flagged: "; showPerc3 (os, nflagged, totalnpoints); os << " (" << nflagged << " out of " << totalnpoints @@ -287,6 +291,10 @@ namespace LOFAR { void FlagCounter::showCorrelation (ostream& os, int64 ntimes) const { int64 ntotal = ntimes * itsBLCounts.size() * itsChanCounts.size(); + // Prevent division by zero + if (ntotal == 0) { + ntotal = 1; + } os << endl << "Percentage of flagged visibilities detected per correlation:" << endl; diff --git a/CEP/DP3/DPPP/src/GainCal.cc b/CEP/DP3/DPPP/src/GainCal.cc index a3863457e685d66bdb5357079b22a2ebff6fc6ff..143beefe32b6a9068231cdf92d855a5aa032f968 100644 --- a/CEP/DP3/DPPP/src/GainCal.cc +++ b/CEP/DP3/DPPP/src/GainCal.cc @@ -24,6 +24,8 @@ #include <lofar_config.h> #include <DPPP/GainCal.h> #include <DPPP/Simulate.h> +#include <DPPP/ApplyCal.h> +#include <DPPP/phasefitter.h> #include <DPPP/CursorUtilCasa.h> #include <DPPP/DPBuffer.h> #include <DPPP/DPInfo.h> @@ -69,21 +71,28 @@ namespace LOFAR { itsUseModelColumn(parset.getBool (prefix + "usemodelcolumn", false)), itsParmDBName (parset.getString (prefix + "parmdb", "")), itsMode (parset.getString (prefix + "caltype")), - itsTStep (0), itsDebugLevel (parset.getInt (prefix + "debuglevel", 0)), itsDetectStalling (parset.getBool (prefix + "detectstalling", true)), + itsApplySolution (parset.getBool (prefix + "applysolution", false)), itsBaselines (), + itsBaselineSelection (parset, prefix), itsMaxIter (parset.getInt (prefix + "maxiter", 50)), itsTolerance (parset.getDouble (prefix + "tolerance", 1.e-5)), - itsPropagateSolutions (parset.getBool(prefix + "propagatesolutions", false)), + itsPropagateSolutions + (parset.getBool(prefix + "propagatesolutions", true)), itsSolInt (parset.getInt(prefix + "solint", 1)), itsNChan (parset.getInt(prefix + "nchan", 0)), itsNFreqCells (0), itsMinBLperAnt (parset.getInt(prefix + "minblperant", 4)), + itsTimeSlotsPerParmUpdate + (parset.getInt(prefix + "timeslotsperparmupdate", 500)), itsConverged (0), itsNonconverged (0), + itsFailed (0), itsStalled (0), - itsNTimes (0) + itsStepInParmUpdate (0), + itsChunkStartTime(0), + itsStepInSolInt (0) { if (itsParmDBName=="") { itsParmDBName=parset.getString("msin")+"/instrument"; @@ -103,8 +112,19 @@ namespace LOFAR { itsApplyBeamStep.setNextStep(DPStep::ShPtr(itsResultStep)); } } + + itsNIter.resize(4,0); + + if (itsApplySolution) { + itsBuf.resize(itsSolInt); + } else { + itsBuf.resize(1); + } + ASSERT(itsMode=="diagonal" || itsMode=="phaseonly" || - itsMode=="fulljones" || itsMode=="scalarphase"); + itsMode=="fulljones" || itsMode=="scalarphase" || + itsMode=="amplitudeonly" || itsMode=="scalaramplitude" || + itsMode=="tecandphase" || itsMode=="tec"); } GainCal::~GainCal() @@ -124,8 +144,10 @@ namespace LOFAR { } else { itsPredictStep.updateInfo(infoIn); } - info().setWriteData(); - info().setWriteFlags(); + if (itsApplySolution) { + info().setWriteData(); + info().setWriteFlags(); + } for (uint i=0; i<nBl; ++i) { itsBaselines.push_back (Baseline(info().getAnt1()[i], @@ -136,8 +158,6 @@ namespace LOFAR { itsSolInt=info().ntime(); } - itsSols.reserve(info().ntime()); - if (itsNChan==0) { itsNChan = info().nchan(); } @@ -149,9 +169,33 @@ namespace LOFAR { itsNFreqCells++; } - itsAntennaUsedNames.resize(info().antennaUsed().size()); - for (int ant=0, nAnts=info().antennaUsed().size(); ant<nAnts; ++ant) { - itsAntennaUsedNames[ant]=info().antennaNames()[info().antennaUsed()[ant]]; + itsSols.reserve(itsTimeSlotsPerParmUpdate); + + // Initialize phase fitters, set their frequency data + if (itsMode=="tecandphase" || itsMode=="tec") { + itsTECSols.reserve(itsTimeSlotsPerParmUpdate); + + itsPhaseFitters.reserve(itsNFreqCells); // TODO: could be numthreads instead + vector<double> freqData(itsNFreqCells); + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + double meanfreq=0; + uint chmin=itsNChan*freqCell; + uint chmax=min(info().nchan(), chmin+itsNChan); + + meanfreq = std::accumulate(info().chanFreqs().data()+chmin, + info().chanFreqs().data()+chmax, 0.0); + + freqData[freqCell] = meanfreq / (chmax-chmin); + } + + uint nSt=info().antennaNames().size(); + for (uint st=0; st<nSt; ++st) { + itsPhaseFitters.push_back(CountedPtr<PhaseFitter>(new PhaseFitter(itsNFreqCells))); + double* nu = itsPhaseFitters[st]->FrequencyData(); + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + nu[freqCell] = freqData[freqCell]; + } + } } iS.reserve(itsNFreqCells); @@ -160,10 +204,14 @@ namespace LOFAR { if ((freqCell+1)*itsNChan>info().nchan()) { // Last cell can be smaller chMax-=((freqCell+1)*itsNChan)%info().nchan(); } - iS.push_back(StefCal(itsSolInt, chMax, itsMode, itsTolerance, - info().antennaNames().size(), itsDetectStalling, - itsDebugLevel)); + iS.push_back(StefCal(itsSolInt, chMax, itsMode, + itsTolerance, info().antennaNames().size(), + itsDetectStalling, itsDebugLevel)); } + + itsFlagCounter.init(getInfo()); + + itsChunkStartTime = info().startTime(); } void GainCal::show (std::ostream& os) const @@ -176,13 +224,16 @@ namespace LOFAR { os << " (will be created)"; } os << endl; - os << " solint: " << itsSolInt <<endl; - os << " nchan: " << itsNChan <<endl; - os << " max iter: " << itsMaxIter << endl; - os << " tolerance: " << itsTolerance << endl; - os << " mode: " << itsMode << endl; - os << " detect stalling: " << boolalpha << itsDetectStalling << endl; - os << " use model column: " << boolalpha << itsUseModelColumn << endl; + os << " solint: " << itsSolInt <<endl; + os << " nchan: " << itsNChan <<endl; + os << " max iter: " << itsMaxIter << endl; + os << " tolerance: " << itsTolerance << endl; + os << " caltype: " << itsMode << endl; + os << " apply solution: " << boolalpha << itsApplySolution << endl; + os << " propagate solutions: " << boolalpha << itsPropagateSolutions << endl; + os << " timeslotsperparmupdate: " << itsTimeSlotsPerParmUpdate << endl; + os << " detect stalling: " << boolalpha << itsDetectStalling << endl; + os << " use model column: " << boolalpha << itsUseModelColumn << endl; if (!itsUseModelColumn) { itsPredictStep.show(os); } else if (itsApplyBeamToModelColumn) { @@ -209,26 +260,45 @@ namespace LOFAR { FlagCounter::showPerc1 (os, itsTimerSolve.getElapsed(), totaltime); os << " of it spent in estimating gains and computing residuals" << endl; + if (itsMode == "tec" || itsMode == "tecandphase") { + os << " "; + FlagCounter::showPerc1 (os, itsTimerPhaseFit.getElapsed(), totaltime); + os << " of it spent in fitting phases" << endl; + } + os << " "; FlagCounter::showPerc1 (os, itsTimerWrite.getElapsed(), totaltime); os << " of it spent in writing gain solutions to disk" << endl; - os << " "; - os <<"Converged: "<<itsConverged<<", stalled: "<<itsStalled<<", non converged: "<<itsNonconverged<<endl; + os << " "; + os <<"Converged: "<<itsConverged<<", stalled: "<<itsStalled<<", non converged: "<<itsNonconverged<<", failed: "<<itsFailed<<endl; + os << " "; + os <<"Iters converged: " << (itsConverged==0?0:itsNIter[0]/itsConverged); + os << ", stalled: "<< (itsStalled ==0?0:itsNIter[1]/itsStalled); + os << ", non converged: "<<(itsNonconverged==0?0:itsNIter[2]/itsNonconverged); + os << ", failed: "<<(itsFailed==0?0:itsNIter[3]/itsFailed)<<endl; } bool GainCal::process (const DPBuffer& bufin) { itsTimer.start(); - itsBuf.referenceFilled (bufin); - itsInput->fetchUVW(bufin, itsBuf, itsTimer); - itsInput->fetchWeights(bufin, itsBuf, itsTimer); - itsInput->fetchFullResFlags(bufin, itsBuf, itsTimer); - Cube<Complex> dataCube=itsBuf.getData(); + uint bufIndex=0; + + if (itsApplySolution) { + bufIndex=itsStepInSolInt; + itsBuf[bufIndex].copy(bufin); + } else { + itsBuf[0].referenceFilled (bufin); + } + itsInput->fetchUVW(bufin, itsBuf[bufIndex], itsTimer); + itsInput->fetchWeights(bufin, itsBuf[bufIndex], itsTimer); + itsInput->fetchFullResFlags(bufin, itsBuf[bufIndex], itsTimer); + + Cube<Complex> dataCube=itsBuf[bufIndex].getData(); Complex* data=dataCube.data(); - float* weight = itsBuf.getWeights().data(); - const Bool* flag=itsBuf.getFlags().data(); + float* weight = itsBuf[bufIndex].getWeights().data(); + const Bool* flag=itsBuf[bufIndex].getFlags().data(); // Simulate. // @@ -238,30 +308,29 @@ namespace LOFAR { itsTimerPredict.start(); if (itsUseModelColumn) { - itsInput->getModelData (itsBuf.getRowNrs(), itsModelData); - if (itsApplyBeamToModelColumn) { + itsInput->getModelData (itsBuf[bufIndex].getRowNrs(), itsModelData); + if (itsApplyBeamToModelColumn) { // TODO: double check this // Temporarily put model data in data column for applybeam step // ApplyBeam step will copy the buffer so no harm is done - itsBuf.getData()=itsModelData; - itsApplyBeamStep.process(itsBuf); + itsBuf[bufIndex].getData()=itsModelData; + itsApplyBeamStep.process(itsBuf[bufIndex]); //Put original data back in data column - itsBuf.getData()=dataCube; + itsBuf[bufIndex].getData()=dataCube; } } else { // Predict - itsPredictStep.process(itsBuf); + itsPredictStep.process(itsBuf[bufIndex]); } itsTimerPredict.stop(); itsTimerFill.start(); - if (itsNTimes==0) { + if (itsStepInSolInt==0) { // Start new solution interval for (uint freqCell=0; freqCell<itsNFreqCells; freqCell++) { - uint nSt=setAntennaMaps(flag, freqCell); - //cout<<"nSt="<<nSt<<endl; - iS[freqCell].resetVis(nSt); + setAntennaMaps(flag, freqCell); + iS[freqCell].resetVis(); } } @@ -273,22 +342,98 @@ namespace LOFAR { } itsTimerFill.stop(); - if (itsNTimes==itsSolInt-1) { + if (itsStepInSolInt==itsSolInt-1) { // Solve past solution interval stefcal(); - itsNTimes=0; + itsStepInParmUpdate++; + + if (itsApplySolution) { + Cube<DComplex> invsol = invertSol(itsSols.back()); + for (uint stepInSolInt=0; stepInSolInt<itsSolInt; stepInSolInt++) { + applySolution(itsBuf[stepInSolInt], invsol); + getNextStep()->process(itsBuf[stepInSolInt]); + } + } + + itsStepInSolInt=0; } else { - itsNTimes++; + itsStepInSolInt++; } itsTimer.stop(); - itsTStep++; - getNextStep()->process(itsBuf); + + if (itsStepInParmUpdate == itsTimeSlotsPerParmUpdate) { + writeSolutions(itsChunkStartTime); + itsChunkStartTime += itsSolInt * itsTimeSlotsPerParmUpdate * info().timeInterval(); + itsSols.clear(); + itsTECSols.clear(); + itsStepInParmUpdate = 0; + } + + if (!itsApplySolution) { + getNextStep()->process(itsBuf[bufIndex]); + } return false; } + Cube<DComplex> GainCal::invertSol(const Cube<DComplex>& sol) { + Cube<DComplex> invsol = sol.copy(); + uint nCr = invsol.shape()[0]; + + // Invert copy of solutions + uint nSt = invsol.shape()[1]; + for (uint st=0; st<nSt; ++st) { + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + if (nCr==4) { + ApplyCal::invert(&invsol(0,st,freqCell)); + } else { + for (uint cr=0; cr<nCr; ++cr) { + invsol(cr, st, freqCell) = 1./invsol(cr, st, freqCell); + } + } + } + } + + return invsol; + } + + void GainCal::applySolution(DPBuffer& buf, const Cube<DComplex>& invsol) { + uint nbl = buf.getData().shape()[2]; + Complex* data = buf.getData().data(); + float* weight = buf.getWeights().data(); // Not initialized yet + bool* flag = buf.getFlags().data(); + uint nchan = buf.getData().shape()[1]; + + uint nCr = invsol.shape()[0]; + + for (size_t bl=0; bl<nbl; ++bl) { + for (size_t chan=0;chan<nchan;chan++) { + uint antA = info().getAnt1()[bl]; + uint antB = info().getAnt2()[bl]; + uint freqCell = chan / itsNChan; + if (nCr>2) { + ApplyCal::applyFull( &invsol(0, antA, freqCell), + &invsol(0, antB, freqCell), + &data[bl * 4 * nchan + chan * 4 ], + &weight[bl * 4 * nchan + chan * 4 ], // Not passing weights, any pointer should do + &flag[ bl * 4 * nchan + chan * 4 ], + bl, chan, false, itsFlagCounter); // Update weights is disabled here + } + else { + ApplyCal::applyDiag( &invsol(0, antA, freqCell), + &invsol(0, antB, freqCell), + &data[bl * 4 * nchan + chan * 4 ], + &weight[bl * 4 * nchan + chan * 4 ], // Not passing weights, any pointer should do + &flag[ bl * 4 * nchan + chan * 4 ], + bl, chan, false, itsFlagCounter); // Update weights is disabled here + } + } + } + } + // Fills itsVis and itsMVis as matrices with all 00 polarizations in the // top left, all 11 polarizations in the bottom right, etc. + // For TEC fitting, it also sets weights for the frequency cells void GainCal::fillMatrices (casa::Complex* model, casa::Complex* data, float* weight, const casa::Bool* flag) { const size_t nBl = info().nbaselines(); @@ -297,25 +442,32 @@ namespace LOFAR { for (uint ch=0;ch<nCh;++ch) { for (uint bl=0;bl<nBl;++bl) { - int ant1=iS[ch/itsNChan].getAntMap()[info().getAnt1()[bl]]; - int ant2=iS[ch/itsNChan].getAntMap()[info().getAnt2()[bl]]; - if (ant1==ant2 || ant1==-1 || ant2 == -1 || flag[bl*nCr*nCh+ch*nCr]) { // Only check flag of cr==0 + int ant1=info().getAnt1()[bl]; + int ant2=info().getAnt2()[bl]; + if (ant1==ant2 || + iS[ch/itsNChan].getStationFlagged()[ant1] || + iS[ch/itsNChan].getStationFlagged()[ant2] || + flag[bl*nCr*nCh+ch*nCr]) { // Only check flag of cr==0 continue; } + if (itsMode=="tec" || itsMode=="tecandphase") { + iS[ch/itsNChan].incrementWeight(weight[bl*nCr*nCh+ch*nCr]); + } + for (uint cr=0;cr<nCr;++cr) { - iS[ch/itsNChan].getVis() (IPosition(6,ant1,cr/2,itsNTimes,ch%itsNChan,cr%2,ant2)) = + iS[ch/itsNChan].getVis() (IPosition(6,ant1,cr/2,itsStepInSolInt,ch%itsNChan,cr%2,ant2)) = DComplex(data [bl*nCr*nCh+ch*nCr+cr]) * DComplex(sqrt(weight[bl*nCr*nCh+ch*nCr+cr])); - iS[ch/itsNChan].getMVis()(IPosition(6,ant1,cr/2,itsNTimes,ch%itsNChan,cr%2,ant2)) = + iS[ch/itsNChan].getMVis()(IPosition(6,ant1,cr/2,itsStepInSolInt,ch%itsNChan,cr%2,ant2)) = DComplex(model[bl*nCr*nCh+ch*nCr+cr]) * DComplex(sqrt(weight[bl*nCr*nCh+ch*nCr+cr])); // conjugate transpose - iS[ch/itsNChan].getVis() (IPosition(6,ant2,cr%2,itsNTimes,ch%itsNChan,cr/2,ant1)) = + iS[ch/itsNChan].getVis() (IPosition(6,ant2,cr%2,itsStepInSolInt,ch%itsNChan,cr/2,ant1)) = DComplex(conj(data [bl*nCr*nCh+ch*nCr+cr])) * DComplex(sqrt(weight[bl*nCr*nCh+ch*nCr+cr])); - iS[ch/itsNChan].getMVis()(IPosition(6,ant2,cr%2,itsNTimes,ch%itsNChan,cr/2,ant1)) = + iS[ch/itsNChan].getMVis()(IPosition(6,ant2,cr%2,itsStepInSolInt,ch%itsNChan,cr/2,ant1)) = DComplex(conj(model[bl*nCr*nCh+ch*nCr+cr] )) * DComplex(sqrt(weight[bl*nCr*nCh+ch*nCr+cr])); } @@ -323,160 +475,292 @@ namespace LOFAR { } } - uint GainCal::setAntennaMaps (const Bool* flag, uint freqCell) { + void GainCal::setAntennaMaps (const Bool* flag, uint freqCell) { uint nCr=info().ncorr(); uint nCh=info().nchan(); uint nBl=info().nbaselines(); + iS[freqCell].clearStationFlagged(); + casa::Vector<casa::uInt> dataPerAntenna; // nAnt dataPerAntenna.resize(info().antennaNames().size()); - dataPerAntenna=0; - for (uint bl=0;bl<nBl;++bl) { - uint ant1=info().getAnt1()[bl]; - uint ant2=info().getAnt2()[bl]; - if (ant1==ant2) { - continue; - } - uint chmax=min((freqCell+1)*itsNChan, nCh); - for (uint ch=freqCell*itsNChan;ch<chmax;++ch) { - for (uint cr=0;cr<nCr;++cr) { - if (!flag[bl*nCr*nCh + ch*nCr + cr]) { - dataPerAntenna(ant1)++; - dataPerAntenna(ant2)++; + // TODO: implement smarter graph algorithm here that requires + // less than O(n_ant^3) worst case operations. + bool flaggedAntsAdded=true; + uint loopcount=0; + while (flaggedAntsAdded) { + dataPerAntenna=0; + for (uint bl=0;bl<nBl;++bl) { + uint ant1=info().getAnt1()[bl]; + uint ant2=info().getAnt2()[bl]; + if (ant1==ant2) { + continue; + } + uint chmax=min((freqCell+1)*itsNChan, nCh); + for (uint ch=freqCell*itsNChan;ch<chmax;++ch) { + for (uint cr=0;cr<nCr;++cr) { + if (!(flag[bl*nCr*nCh + ch*nCr + cr] + || iS[freqCell].getStationFlagged()[ant1] + || iS[freqCell].getStationFlagged()[ant2] + )) { + dataPerAntenna(ant1)++; + dataPerAntenna(ant2)++; + } } } } - } - - uint nSt=0; - for (uint ant=0; ant<info().antennaNames().size(); ++ant) { - if (dataPerAntenna(ant)>nCr*itsMinBLperAnt) { - iS[freqCell].getAntMap()[ant]=nSt++; // Index in stefcal numbering - } else { - iS[freqCell].getAntMap()[ant]=-1; // Not enough data + flaggedAntsAdded=false; + for (uint ant=0; ant<info().antennaNames().size(); ++ant) { + if (dataPerAntenna(ant)>nCr*itsMinBLperAnt) { + iS[freqCell].getStationFlagged()[ant]=false; // Index in stefcal numbering + } else { // Not enough data + //cout<<"flagging station "<<ant<<", "<<dataPerAntenna(ant)<<endl; + if (!iS[freqCell].getStationFlagged()[ant]) { + iS[freqCell].getStationFlagged()[ant]=true; + flaggedAntsAdded=true; + } + } } + loopcount++; } - - return nSt; } void GainCal::stefcal () { itsTimerSolve.start(); for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { - iS[freqCell].init(); + if (itsPropagateSolutions) { + iS[freqCell].init(false); + } else { + iS[freqCell].init(true); + } } uint iter=0; + casa::Matrix<double> tecsol(itsMode=="tecandphase"?2:1, info().antennaNames().size(), 0); + std::vector<StefCal::Status> converged(itsNFreqCells,StefCal::NOTCONVERGED); for (;iter<itsMaxIter;++iter) { bool allConverged=true; +#pragma omp parallel for for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { if (converged[freqCell]==StefCal::CONVERGED) { // Do another step when stalled and not all converged continue; } converged[freqCell] = iS[freqCell].doStep(iter); - if (converged[freqCell]==StefCal::NOTCONVERGED) { + if (converged[freqCell]==StefCal::NOTCONVERGED) { // Only continue if there are steps worth continuing (so not converged, failed or stalled) allConverged = false; + } + } + + if (itsMode=="tec" || itsMode=="tecandphase") { + itsTimerSolve.stop(); + itsTimerPhaseFit.start(); + casa::Matrix<casa::DComplex> sols_f(itsNFreqCells, info().antennaNames().size()); + + uint nSt = info().antennaNames().size(); + + // TODO: set phase reference so something smarter that station 0 + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + casa::Matrix<casa::DComplex> sol = iS[freqCell].getSolution(false); + if (iS[freqCell].getStationFlagged()[0]) { + // If reference station flagged, flag whole channel + for (uint st=0; st<info().antennaNames().size(); ++st) { + iS[freqCell].getStationFlagged()[st] = true; + } + } else { + for (uint st=0; st<info().antennaNames().size(); ++st) { + sols_f(freqCell, st) = sol(st, 0)/sol(0, 0); + ASSERT(isFinite(sols_f(freqCell, st))); + } + } } - if (allConverged) { - break; + +#pragma omp parallel for + for (uint st=0; st<nSt; ++st) { + uint numpoints=0; + double cost=0; + double* phases = itsPhaseFitters[st]->PhaseData(); + double* weights = itsPhaseFitters[st]->WeightData(); + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + if (iS[freqCell].getStationFlagged()[st%nSt] || + converged[freqCell]==StefCal::FAILED) { + phases[freqCell] = 0; + weights[freqCell] = 0; + } else { + phases[freqCell] = arg(sols_f(freqCell, st)); + if (!isFinite(phases[freqCell])) { + cout<<"Yuk, phases[freqCell]="<<phases[freqCell]<<", sols_f(freqCell, st)="<<sols_f(freqCell, st)<<endl; + ASSERT(isFinite(phases[freqCell])); + } + ASSERT(iS[freqCell].getWeight()>0); + weights[freqCell] = iS[freqCell].getWeight(); + if (itsDebugLevel > 0 && st==34) { + cout<<"w["<<freqCell<<"]="<<weights[freqCell]<<endl; + } + numpoints++; + } + } + + if (itsDebugLevel>0) { + cout<<"st="<<st<<", numpoints="<<numpoints<<endl; + } + + if (itsDebugLevel>0 && st==34) { + cout<<"t="<<itsStepInParmUpdate<<", st="<<st<<", unfitted["<<iter<<"]=["; + uint freqCell2=0; + for (; freqCell2<itsNFreqCells-1; ++freqCell2) { + cout<<phases[freqCell2]<<","; + } + cout<<phases[freqCell2]<<"];"<<endl; + } + + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + ASSERT(isFinite(phases[freqCell])); + } + + if (numpoints>1) { // TODO: limit should be higher + //cout<<"tecsol(0,"<<st<<")="<<tecsol(0,st)<<", tecsol(1,"<<st<<")="<<tecsol(1,st)<<endl; + if (itsMode=="tecandphase") { + cost=itsPhaseFitters[st]->FitDataToTEC2Model(tecsol(0, st), tecsol(1,st)); + } else { // itsMode=="tec" + cost=itsPhaseFitters[st]->FitDataToTEC1Model(tecsol(0, st)); + } + // Update solution in stefcal + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + ASSERT(isFinite(phases[freqCell])); + iS[freqCell].getSolution(false)(st, 0) = polar(1., phases[freqCell]); + } + } else { + tecsol(0, st) = 0; //std::numeric_limits<double>::quiet_NaN(); + if (itsMode=="tecandphase") { + tecsol(1, st) = 0; //std::numeric_limits<double>::quiet_NaN(); + } + } + + if (st==34 && itsDebugLevel>0) { + cout<<"fitted["<<st<<"]=["; + uint freqCell=0; + for (; freqCell<itsNFreqCells-1; ++freqCell) { + cout<<phases[freqCell]<<","; + } + cout<<phases[freqCell]<<"]"<<endl; + if (itsMode=="tecandphase") { + cout << "fitdata["<<st<<"]=[" << tecsol(0,1) << ", " << tecsol(1,1) << ", " << cost << "];" << endl; + } else { + cout << "fitdata["<<st<<"]=[" << tecsol(0,1) << "];" << endl; + } + } } - } } // End niter + itsTimerPhaseFit.stop(); + itsTimerSolve.start(); + } + + if (allConverged) { + break; + } + +#ifdef DEBUG + if (itsDebugLevel>1) { // Only antenna 1 + cout<<"phases["<<iter<<"]=["; + uint freqCell=0; + for (; freqCell<itsNFreqCells-1; ++freqCell) { + cout<<arg(iS[freqCell].getSolution(false)(1, 0)/iS[freqCell].getSolution(false)(0,0))<<","; + } + cout<<arg(iS[freqCell].getSolution(false)(1, 0)/iS[freqCell].getSolution(false)(0,0))<<"];"<<endl; + } + if (itsDebugLevel>2) { // Statistics about nStalled + cout<<"convstatus["<<iter<<"]=["; + uint freqCell=0; + for (; freqCell<itsNFreqCells-1; ++freqCell) { + cout<<converged[freqCell]<<","; + } + cout<<converged[freqCell]<<"]"<<endl; + } +#endif + } // End niter for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { switch (converged[freqCell]) { - case StefCal::CONVERGED: {itsConverged++; break;} - case StefCal::STALLED: {itsStalled++; break;} - case StefCal::NOTCONVERGED: {itsStalled++; break;} + case StefCal::CONVERGED: {itsConverged++; itsNIter[0]+=iter; break;} + case StefCal::STALLED: {itsStalled++; itsNIter[1]+=iter; break;} + case StefCal::NOTCONVERGED: {itsNonconverged++; itsNIter[2]+=iter; break;} + case StefCal::FAILED: {itsFailed++; itsNIter[3]+=iter; break;} default: THROW(Exception, "Unknown converged status"); } } // Stefcal terminated (either by maxiter or by converging) - // Let's save G... - Cube<DComplex> allg(info().antennaNames().size(), iS[0].numCorrelations(), itsNFreqCells); + + Cube<DComplex> sol(iS[0].numCorrelations(), info().antennaNames().size(), itsNFreqCells); + + uint transpose[2][4] = { { 0, 1, 0, 0 }, { 0, 2, 1, 3 } }; + + uint nSt = info().antennaNames().size(); + for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { - //cout<<endl<<"freqCell="<<freqCell<<", timeCell="<<itsTStep/itsSolInt<<", tstep="<<itsTStep<<endl; - casa::Matrix<casa::DComplex> sol = iS[freqCell].getSolution(); - for (uint st=0; st<info().antennaNames().size(); st++) { - for (uint cr=0; cr<iS[0].numCorrelations(); ++cr) { - allg(st,cr,freqCell)=sol(st,cr); + casa::Matrix<casa::DComplex> tmpsol = iS[freqCell].getSolution(true); + + for (uint st=0; st<nSt; st++) { + for (uint cr=0; cr<iS[0].nCr(); ++cr) { + uint crt=transpose[iS[0].numCorrelations()/4][cr]; // Conjugate transpose ! (only for numCorrelations = 4) + sol(crt, st, freqCell) = conj(tmpsol(st, cr)); // Conjugate transpose + if (itsMode=="diagonal" || itsMode=="phaseonly" || itsMode=="amplitudeonly") { + sol(crt+1, st, freqCell) = conj(tmpsol(st+nSt,cr)); // Conjugate transpose + } } } } - itsSols.push_back(allg); - - itsTimerSolve.stop(); - } - - void GainCal::finish() - { - itsTimer.start(); - - //Solve remaining time slots if any - if (itsNTimes!=0) { - stefcal(); + itsSols.push_back(sol); + if (itsMode=="tec" || itsMode=="tecandphase") { + itsTECSols.push_back(tecsol); } - itsTimerWrite.start(); + itsTimerSolve.stop(); + } // End stefcal() - uint nSt=info().antennaNames().size(); - uint ntime=itsSols.size(); + void GainCal::initParmDB() { + itsParmDB = boost::shared_ptr<BBS::ParmDB> + (new BBS::ParmDB(BBS::ParmDBMeta("casa", itsParmDBName), + false)); + itsParmDB->lock(); + // Store the (freq, time) resolution of the solutions. - // Construct solution grid. - const Vector<double>& freq = getInfo().chanFreqs(); - const Vector<double>& freqWidth = getInfo().chanWidths(); - BBS::Axis::ShPtr freqAxis( - new BBS::RegularAxis( - freq[0] - freqWidth[0] * 0.5, - freqWidth[0]*itsNChan, - itsNFreqCells)); - BBS::Axis::ShPtr timeAxis( - new BBS::RegularAxis( - info().startTime(), - info().timeInterval() * itsSolInt, - ntime)); - BBS::Grid solGrid(freqAxis, timeAxis); - // Create domain grid. - BBS::Axis::ShPtr tdomAxis( - new BBS::RegularAxis( - info().startTime(), - info().timeInterval() * itsSolInt * ntime, - 1)); - BBS::Axis::ShPtr fdomAxis( - new BBS::RegularAxis( - freq[0] - freqWidth[0] * 0.5, - getInfo().totalBW(), 1)); - BBS::Grid domainGrid(fdomAxis, tdomAxis); + double freqWidth = getInfo().chanWidths()[0]; + if (getInfo().chanFreqs().size()>1) { // Handle data with evenly spaced gaps between channels + freqWidth = info().chanFreqs()[1]-info().chanFreqs()[0]; + } - // Open the ParmDB at the first write. - // In that way the instrumentmodel ParmDB can be in the MS directory. - if (! itsParmDB) { - itsParmDB = boost::shared_ptr<BBS::ParmDB> - (new BBS::ParmDB(BBS::ParmDBMeta("casa", itsParmDBName), - false)); - itsParmDB->lock(); - // Store the (freq, time) resolution of the solutions. - vector<double> resolution(2); - resolution[0] = freqWidth[0]; - resolution[1] = info().timeInterval() * itsSolInt; - itsParmDB->setDefaultSteps(resolution); - string name=(itsMode=="commonscalarphase"?"CommonScalarPhase:*":"Gain:*"); - if (!itsParmDB->getNames(name).empty()) { - DPLOG_WARN_STR ("Solutions for "<<name<<" already in "<<itsParmDBName - <<", these are removed"); - itsParmDB->deleteValues(name, BBS::Box( - freqAxis->start(), tdomAxis->start(), - freqAxis->end(), tdomAxis->end(), true - ) - ); - } + vector<double> resolution(2); + resolution[0] = freqWidth * itsNChan; + resolution[1] = info().timeInterval() * itsSolInt; + itsParmDB->setDefaultSteps(resolution); + + string parmname=parmName()+"*"; + + if (!itsParmDB->getNames(parmname).empty()) { + DPLOG_WARN_STR ("Solutions for "<<parmname<<" already in "<<itsParmDBName + <<", these are removed"); + // Specify entire domain of this MS; only to specify that the existing + // values should be deleted for this domain + BBS::Axis::ShPtr tdomAxis( + new BBS::RegularAxis( + info().startTime(), + info().ntime() * info().timeInterval(), + 1)); + BBS::Axis::ShPtr fdomAxis( + new BBS::RegularAxis( + info().chanFreqs()[0] - freqWidth * 0.5, + freqWidth * getInfo().chanFreqs().size(), 1)); + + itsParmDB->deleteValues(parmname, BBS::Box( + fdomAxis->start(), tdomAxis->start(), + fdomAxis->end(), tdomAxis->end(), true)); } // Write out default values, if they don't exist yet @@ -492,6 +776,16 @@ namespace LOFAR { } } + // Write out default phases + if (itsMode=="amplitudeonly" || itsMode=="scalaramplitude") { + itsParmDB->getDefValues(parmset, "Gain:0:0:Phase"); + if (parmset.empty()) { + ParmValueSet pvset(ParmValue(0.0)); + itsParmDB->putDefValue("Gain:0:0:Phase",pvset); + itsParmDB->putDefValue("Gain:1:1:Phase",pvset); + } + } + // Write out default gains if (itsMode=="diagonal" || itsMode=="fulljones") { itsParmDB->getDefValues(parmset, "Gain:0:0:Real"); @@ -501,64 +795,170 @@ namespace LOFAR { itsParmDB->putDefValue("Gain:1:1:Real",pvset); } } + } + + string GainCal::parmName() { + string name; + if (itsMode=="scalarphase") { + name=string("CommonScalarPhase:"); + } else if (itsMode=="scalaramplitude") { + name=string("CommonScalarAmplitude:"); + } else if (itsMode=="tec" || itsMode=="tecandphase") { + name=string("TEC:"); + } + else { + name=string("Gain:"); + } + + return name; + } + + void GainCal::writeSolutions(double startTime) { + itsTimer.start(); + itsTimerWrite.start(); + + // Open the ParmDB at the first write. + // In that way the instrumentmodel ParmDB can be in the MS directory. + if (!itsParmDB) { + initParmDB(); + } // End initialization of parmdb + + uint ntime=itsSols.size(); + uint nchan, nfreqs; + if (itsMode=="tec" || itsMode=="tecandphase") { + nfreqs = 1; + nchan = info().nchan(); + } else { + nfreqs = itsNFreqCells; + nchan = itsNChan; + } + + // Construct solution grid for the current chunk + double freqWidth = getInfo().chanWidths()[0]; + if (getInfo().chanFreqs().size()>1) { // Handle data with evenly spaced gaps between channels + freqWidth = info().chanFreqs()[1]-info().chanFreqs()[0]; + } + + // Get end time of the current chunk. For the last chunk, this + // is chopped off at the end of the MS (only if solint > 1) + double endTime = min(startTime + ntime * info().timeInterval() * itsSolInt, + info().startTime() + info().ntime() * info().timeInterval()); + + // Make time axis (can be non regular for last chunk if solint > 1) + vector<double> lowtimes(ntime), hightimes(ntime); + for (uint t=0; t<ntime; ++t) { + lowtimes[t] = startTime + info().timeInterval() * itsSolInt * t; + hightimes[t] = min(startTime + info().timeInterval() * itsSolInt * (t+1), + endTime); + } + BBS::Axis::ShPtr timeAxis = Axis::makeAxis(lowtimes, hightimes); + + BBS::Axis::ShPtr freqAxis( + new BBS::RegularAxis( + getInfo().chanFreqs()[0] - freqWidth * 0.5, + freqWidth*nchan, + nfreqs)); + BBS::Grid solGrid(freqAxis, timeAxis); + + // Construct domain grid for the current chunk + BBS::Axis::ShPtr tdomAxis( + new BBS::RegularAxis( + startTime, + endTime - startTime, + 1)); + BBS::Axis::ShPtr fdomAxis( + new BBS::RegularAxis( + info().chanFreqs()[0] - freqWidth * 0.5, + freqWidth * getInfo().chanFreqs().size(), 1)); + BBS::Grid domainGrid(fdomAxis, tdomAxis); // Write the solutions per parameter. - const char* str0101[] = {"0:0:","1:0:","0:1:","1:1:"}; // Conjugate transpose! + const char* str0101[] = {"0:0:","0:1:","1:0:","1:1:"}; const char* strri[] = {"Real:","Imag:"}; - Matrix<double> values(itsNFreqCells, ntime); + Matrix<double> values(nfreqs, ntime); DComplex sol; - for (size_t st=0; st<nSt; ++st) { - uint seqnr = 0; // To take care of real and imaginary part - string suffix(info().antennaNames()[st]); + uint nSt=info().antennaNames().size(); + for (size_t st=0; st<nSt; ++st) { for (int pol=0; pol<4; ++pol) { // For 0101 - if ((itsMode=="diagonal" || itsMode=="phaseonly") && (pol==1||pol==2)) { + if ((itsMode=="diagonal" || itsMode=="phaseonly" || + itsMode=="amplitudeonly") && (pol==1||pol==2)) { continue; } - if (itsMode=="scalarphase" && pol>0) { + if ((itsMode=="scalarphase" || itsMode=="scalaramplitude" || + itsMode=="tec" || itsMode=="tecandphase") && pol>0) { continue; } - int realimmax; - if (itsMode=="phaseonly" || itsMode=="scalarphase") { + int realimmax; // For tecandphase, this functions as dummy between tec and commonscalarphase + if (itsMode=="phaseonly" || itsMode=="scalarphase" || + itsMode=="amplitudeonly" || itsMode=="scalaramplitude" || itsMode=="tec") { realimmax=1; } else { realimmax=2; } for (int realim=0; realim<realimmax; ++realim) { // For real and imaginary - string name(string("Gain:") + - str0101[pol] + (itsMode=="phaseonly"?"Phase:":strri[realim]) + suffix); - if (itsMode=="scalarphase") { - name="CommonScalarPhase:"+suffix; + string name = parmName(); + + if (itsMode!="scalarphase" && itsMode!="scalaramplitude") { + name+=str0101[pol]; + if (itsMode=="phaseonly") { + name=name+"Phase:"; + } else if (itsMode=="amplitudeonly") { + name=name+"Ampl:"; + } else { + name=name+strri[realim]; + } } + if (itsMode=="tecandphase" || itsMode=="tec") { + if (realim==0) { + name="TEC:"; + } else { + name="CommonScalarPhase:"; + } + } + + name+=info().antennaNames()[st]; + // Collect its solutions for all times and frequency cells in a single array. for (uint ts=0; ts<ntime; ++ts) { - for (uint freqCell=0; freqCell<itsNFreqCells; ++freqCell) { + for (uint freqCell=0; freqCell<nfreqs; ++freqCell) { if (itsMode=="fulljones") { - if (seqnr%2==0) { - values(freqCell, ts) = real(itsSols[ts](st,seqnr/2,freqCell)); + if (realim==0) { + values(freqCell, ts) = real(itsSols[ts](pol,st,freqCell)); } else { - values(freqCell, ts) = -imag(itsSols[ts](st,seqnr/2,freqCell)); // Conjugate transpose! + values(freqCell, ts) = imag(itsSols[ts](pol,st,freqCell)); } } else if (itsMode=="diagonal") { - if (seqnr%2==0) { - values(freqCell, ts) = real(itsSols[ts](st,pol/3,freqCell)); + if (realim==0) { + values(freqCell, ts) = real(itsSols[ts](pol/3,st,freqCell)); } else { - values(freqCell, ts) = -imag(itsSols[ts](st,pol/3,freqCell)); // Conjugate transpose! + values(freqCell, ts) = imag(itsSols[ts](pol/3,st,freqCell)); } } else if (itsMode=="scalarphase" || itsMode=="phaseonly") { - values(freqCell, ts) = -arg(itsSols[ts](st,pol/3,freqCell)); + values(freqCell, ts) = arg(itsSols[ts](pol/3,st,freqCell)); + } else if (itsMode=="scalaramplitude" || itsMode=="amplitudeonly") { + values(freqCell, ts) = abs(itsSols[ts](pol/3,st,freqCell)); + } else if (itsMode=="tec" || itsMode=="tecandphase") { + if (realim==0) { + values(freqCell, ts) = itsTECSols[ts](realim,st) / 8.44797245e9; + } else { + values(freqCell, ts) = -itsTECSols[ts](realim,st); // TODO: why is there a minus here? + } + } + else { + THROW (Exception, "Unhandled mode"); } } } - //cout.flush(); - seqnr++; BBS::ParmValue::ShPtr pv(new BBS::ParmValue()); pv->setScalars (solGrid, values); + BBS::ParmValueSet pvs(domainGrid, vector<BBS::ParmValue::ShPtr>(1, pv)); map<string,int>::const_iterator pit = itsParmIdMap.find(name); + if (pit == itsParmIdMap.end()) { // First time, so a new nameId will be set. // Check if the name was defined in the parmdb previously @@ -576,10 +976,34 @@ namespace LOFAR { itsTimerWrite.stop(); itsTimer.stop(); + } + + void GainCal::finish() + { + itsTimer.start(); + + //Solve remaining time slots if any + if (itsStepInSolInt!=0) { + stefcal(); + + if (itsApplySolution) { + Cube<DComplex> invsol = invertSol(itsSols.back()); + for (uint stepInSolInt=0; stepInSolInt<itsStepInSolInt; stepInSolInt++) { + applySolution(itsBuf[stepInSolInt], invsol); + getNextStep()->process(itsBuf[stepInSolInt]); + } + } + } + + itsTimer.stop(); + + if (!itsSols.empty()) { + writeSolutions(itsChunkStartTime); + } + // Let the next steps finish. getNextStep()->finish(); } - } //# end namespace } diff --git a/CEP/DP3/DPPP/src/MSReader.cc b/CEP/DP3/DPPP/src/MSReader.cc index 35dd696864e6e0fe5161ea7bc3c79ac7dd9a81be..1b6f205db5ee85197fb6b5372d4edfebb10c31a3 100644 --- a/CEP/DP3/DPPP/src/MSReader.cc +++ b/CEP/DP3/DPPP/src/MSReader.cc @@ -131,7 +131,7 @@ namespace LOFAR { } } // Prepare the MS access and get time info. - double startTime, endTime; + double startTime=0., endTime=0.; prepare (startTime, endTime, itsTimeInterval); // Start and end time can be given in the parset in case leading // or trailing time slots are missing. @@ -258,8 +258,9 @@ namespace LOFAR { itsLastMSTime = mstime; itsIter.next(); } - // Stop if at the end. - if (itsNextTime > itsLastTime && !near(itsNextTime, itsLastTime)) { + // Stop if at the end, or if there is no data at all + if ((itsNextTime > itsLastTime && !near(itsNextTime, itsLastTime)) || + itsNextTime==0.) { return false; } // Fill the buffer. @@ -390,7 +391,7 @@ namespace LOFAR { os << " ncorrelations: " << getInfo().ncorr() << std::endl; uint nrbl = getInfo().nbaselines(); os << " nbaselines: " << nrbl << std::endl; - os << " ntimes: " << itsSelMS.nrow() / nrbl << std::endl; + os << " ntimes: " << (nrbl==0 ? 0 : itsSelMS.nrow() / nrbl) << std::endl; os << " time interval: " << getInfo().timeInterval() << std::endl; os << " DATA column: " << itsDataColName; if (itsMissingData) { @@ -421,7 +422,9 @@ namespace LOFAR { void MSReader::prepare (double& firstTime, double& lastTime, double& interval) { - ASSERT (itsSelMS.nrow() > 0); + if (itsSelMS.nrow() == 0) { + DPLOG_WARN_STR ("The selected input does not contain any data."); + } TableDesc tdesc = itsMS.tableDesc(); itsHasWeightSpectrum = false; @@ -495,9 +498,11 @@ namespace LOFAR { sortms = itsSelMS.sort(sortCols); } // Get first and last time and interval from MS. - firstTime = ROScalarColumn<double>(sortms, "TIME")(0); - lastTime = ROScalarColumn<double>(sortms, "TIME")(sortms.nrow()-1); - interval = ROScalarColumn<double>(sortms, "INTERVAL")(0); + if (itsSelMS.nrow() > 0) { + firstTime = ROScalarColumn<double>(sortms, "TIME")(0); + lastTime = ROScalarColumn<double>(sortms, "TIME")(sortms.nrow()-1); + interval = ROScalarColumn<double>(sortms, "INTERVAL")(0); + } // Create iterator over time. Do not sort again. itsIter = TableIterator (sortms, Block<String>(1, "TIME"), TableIterator::Ascending, diff --git a/CEP/DP3/DPPP/src/MSUpdater.cc b/CEP/DP3/DPPP/src/MSUpdater.cc index e74f07ac4ce5105e649fe7569cfe2f46ead0f54f..b8d72496af1e65e2063db9570464c941200b6ee1 100644 --- a/CEP/DP3/DPPP/src/MSUpdater.cc +++ b/CEP/DP3/DPPP/src/MSUpdater.cc @@ -32,10 +32,12 @@ #include <tables/Tables/ScalarColumn.h> #include <tables/Tables/ArrColDesc.h> #include <tables/Tables/ColumnDesc.h> +#include <tables/Tables/StandardStMan.h> #include <casa/Containers/Record.h> #include <casa/Utilities/LinearSearch.h> #include <ms/MeasurementSets/MeasurementSet.h> #include <iostream> +#include <limits> using namespace casa; @@ -60,6 +62,8 @@ namespace LOFAR { itsDataColName = parset.getString (prefix+"datacolumn", ""); itsWeightColName = parset.getString (prefix+"weightcolumn",""); itsNrTimesFlush = parset.getUint (prefix+"flush", 0); + itsTileSize = parset.getUint (prefix+"tilesize", 1024); + itsStManKeys.Set(parset, prefix); } MSUpdater::~MSUpdater() @@ -76,24 +80,51 @@ namespace LOFAR { return false; } - TableDesc td; - td.addColumn (cd, colName); - - // Use the same data manager as the DATA column. - // Get the data manager info and find the DATA column in it. - Record dminfo = itsMS.dataManagerInfo(); - Record colinfo; - for (uInt i=0; i<dminfo.nfields(); ++i) { - const Record& subrec = dminfo.subRecord(i); - if (linearSearch1 (Vector<String>(subrec.asArrayString("COLUMNS")), - "DATA") >= 0) { - colinfo = subrec; - break; + if(itsStManKeys.stManName == "dysco" && itsStManKeys.dyscoDataBitRate != 0) + { + casa::Record dyscoSpec = itsStManKeys.GetDyscoSpec(); + DataManagerCtor dyscoConstructor = DataManager::getCtor("DyscoStMan"); + CountedPtr<DataManager> dyscoStMan(dyscoConstructor(colName + "_dm", dyscoSpec)); + ColumnDesc directColumnDesc(cd); + directColumnDesc.setOptions(casacore::ColumnDesc::Direct | casacore::ColumnDesc::FixedShape); + TableDesc td; + td.addColumn (directColumnDesc, colName); + itsMS.addColumn (td, *dyscoStMan); + } + else { + // When no specific storage manager is requested, use the same + // as for the DATA column. + // Get the data manager info and find the DATA column in it. + Record dminfo = itsMS.dataManagerInfo(); + Record colinfo; + for (uInt i=0; i<dminfo.nfields(); ++i) { + const Record& subrec = dminfo.subRecord(i); + if (linearSearch1 (Vector<String>(subrec.asArrayString("COLUMNS")), + "DATA") >= 0) { + colinfo = subrec; + break; + } + } + ASSERT(colinfo.nfields()>0); + // When the storage manager is compressed, do not implicitly (re)compress it. Use TiledStMan instead. + std::string dmType = colinfo.asString("TYPE"); + TableDesc td; + td.addColumn (cd, colName); + if(dmType == "DyscoStMan") + { + IPosition tileShape(3, info().ncorr(), info().nchan(), 1); + tileShape[2] = itsTileSize * 1024 / (8 * tileShape[0] * tileShape[1]); + if (tileShape[2] < 1) { + tileShape[2] = 1; + } + TiledColumnStMan tsm(colName + "_dm", tileShape); + itsMS.addColumn (td, tsm); + } + else { + colinfo.define ("NAME", colName + "_dm"); + itsMS.addColumn (td, colinfo); } } - ASSERT(colinfo.nfields()>0); - colinfo.define ("NAME", colName + "_dm"); - itsMS.addColumn (td, colinfo); return true; } @@ -104,7 +135,22 @@ namespace LOFAR { putFlags (buf.getRowNrs(), buf.getFlags()); } if (itsWriteData) { - putData (buf.getRowNrs(), buf.getData()); + // If compressing, flagged values need to be set to NaN to decrease the dynamic range + if(itsStManKeys.stManName == "dysco") + { + casa::Cube<casa::Complex> dataCopy = buf.getData().copy(); + casa::Cube<casa::Complex>::iterator dataIter = dataCopy.begin(); + for(casa::Cube<bool>::const_iterator flagIter = buf.getFlags().begin(); flagIter != buf.getFlags().end(); ++flagIter) + { + if(*flagIter) + *dataIter = casa::Complex(std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()); + ++dataIter; + } + putData (buf.getRowNrs(), dataCopy); + } + else { + putData (buf.getRowNrs(), buf.getData()); + } } if (itsWriteWeights) { if (!buf.getWeights().empty()) { @@ -215,6 +261,17 @@ namespace LOFAR { if (itsWriteWeights) os << " weights"; os << std::endl; } + if(itsStManKeys.stManName == "dysco") { + os + << " Compressed: yes\n" + << " Data bitrate: " << itsStManKeys.dyscoDataBitRate << '\n' + << " Weight bitrate: " << itsStManKeys.dyscoWeightBitRate << '\n' + << " Dysco mode: " << itsStManKeys.dyscoNormalization << ' ' + << itsStManKeys.dyscoDistribution << '(' << itsStManKeys.dyscoDistTruncation << ")\n"; + } + else { + os << " Compressed: no\n"; + } os << std::endl; os << " flush: " << itsNrTimesFlush << std::endl; } diff --git a/CEP/DP3/DPPP/src/MSWriter.cc b/CEP/DP3/DPPP/src/MSWriter.cc index eb2884247f13169e8a2ce946e6068198ddcc43c3..bb708f6d4735eb4f037ec79422041d951a225ce5 100644 --- a/CEP/DP3/DPPP/src/MSWriter.cc +++ b/CEP/DP3/DPPP/src/MSWriter.cc @@ -47,6 +47,7 @@ #include <casa/Containers/Record.h> #include <casa/OS/Path.h> #include <iostream> +#include <limits> using namespace casa; @@ -78,6 +79,8 @@ namespace LOFAR { " can be used as output when writing a new MS"); ASSERTSTR (itsWeightColName == "WEIGHT_SPECTRUM", "Currently only the " "WEIGHT_SPECTRUM column can be used as output when writing a new MS"); + + itsStManKeys.Set(parset, prefix); } MSWriter::~MSWriter() @@ -181,6 +184,17 @@ namespace LOFAR { os << " time interval: " << itsInterval << std::endl; os << " DATA column: " << itsDataColName << std::endl; os << " WEIGHT column: " << itsWeightColName << std::endl; + if(itsStManKeys.stManName == "dysco") { + os + << " Compressed: yes\n" + << " Data bitrate: " << itsStManKeys.dyscoDataBitRate << '\n' + << " Weight bitrate: " << itsStManKeys.dyscoWeightBitRate << '\n' + << " Dysco mode: " << itsStManKeys.dyscoNormalization << ' ' + << itsStManKeys.dyscoDistribution << '(' << itsStManKeys.dyscoDistTruncation << ")\n"; + } + else { + os << " Compressed: no\n"; + } } void MSWriter::showTimings (std::ostream& os, double duration) const @@ -191,19 +205,24 @@ namespace LOFAR { } void MSWriter::makeArrayColumn (ColumnDesc desc, const IPosition& ipos, - TiledColumnStMan* tsm, Table& table) + DataManager* dm, Table& table, bool makeDirectColumn) { desc.setOptions(0); desc.setShape(ipos); - desc.setOptions(ColumnDesc::FixedShape); + if (makeDirectColumn) { + desc.setOptions(ColumnDesc::Direct | ColumnDesc::FixedShape); + } + else { + desc.setOptions(ColumnDesc::FixedShape); + } if (table.tableDesc().isColumn(desc.name())) { table.removeColumn(desc.name()); } - // Use tiled storage manager if given. - if (tsm == 0) { + // Use storage manager if given. + if (dm == 0) { table.addColumn (desc); } else { - table.addColumn (desc, *tsm); + table.addColumn (desc, *dm); } } @@ -300,6 +319,7 @@ namespace LOFAR { // Setup table creation. Exception is thrown if it exists already. Table::TableOption opt = itsOverwrite ? Table::New : Table::NewNoReplace; SetupNewTable newtab(outName, newdesc, opt); + // First bind all columns to SSM. // For all columns defined in dminfo the bindings will be overwritten. // In this way variable columns like ANTENNA1/2 are bound to SSM. @@ -308,22 +328,35 @@ namespace LOFAR { StandardStMan ssm("SSMVar", 32768); newtab.bindAll (ssm); } + // Bind all columns according to dminfo. newtab.bindCreate (dminfo); + DataManagerCtor dyscoConstructor = 0; + Record dyscoSpec; + if(itsStManKeys.stManName == "dysco") { + dyscoSpec = itsStManKeys.GetDyscoSpec(); + dyscoConstructor = DataManager::getCtor("DyscoStMan"); + } itsMS = Table(newtab); - { + + if (itsStManKeys.stManName == "dysco" && itsStManKeys.dyscoDataBitRate != 0) { + // Add DATA column using Dysco stman. + CountedPtr<DataManager> dyscoStMan(dyscoConstructor("DyscoData", dyscoSpec)); + makeArrayColumn (tdesc["DATA"], dataShape, dyscoStMan.get(), itsMS, true); + } + else { // Add DATA column using tsm. TiledColumnStMan tsm("TiledData", tileShape); makeArrayColumn (tdesc["DATA"], dataShape, &tsm, itsMS); } - { - // Add FLAG column using tsm. - // Use larger tile shape because flags are stored as bits. - IPosition tileShapeF(tileShape); - tileShapeF[2] *= 8; - TiledColumnStMan tsmf("TiledFlag", tileShapeF); - makeArrayColumn(tdesc["FLAG"], dataShape, &tsmf, itsMS); - } + + // Add FLAG column using tsm. + // Use larger tile shape because flags are stored as bits. + IPosition tileShapeF(tileShape); + tileShapeF[2] *= 8; + TiledColumnStMan tsmf("TiledFlag", tileShapeF); + makeArrayColumn(tdesc["FLAG"], dataShape, &tsmf, itsMS); + if (itsWriteFullResFlags) { // Add LOFAR_FULL_RES_FLAG column using tsm. // The input can already be averaged and averaging can be done in @@ -337,7 +370,17 @@ namespace LOFAR { dataShapeF, ColumnDesc::FixedShape); makeArrayColumn(padesc, dataShapeF, &tsmf, itsMS); } - { + if (itsStManKeys.stManName == "dysco" && itsStManKeys.dyscoWeightBitRate != 0) { + // Add WEIGHT_SPECTRUM column using Dysco stman. + CountedPtr<DataManager> dyscoStMan(dyscoConstructor( + "DyscoWeightSpectrum", dyscoSpec) + ); + ArrayColumnDesc<float> wsdesc("WEIGHT_SPECTRUM", "weight per corr/chan", + dataShape, + ColumnDesc::FixedShape | ColumnDesc::Direct); + makeArrayColumn (wsdesc, dataShape, dyscoStMan.get(), itsMS, true); + } + else { // Add WEIGHT_SPECTRUM column using tsm. TiledColumnStMan tsmw("TiledWeightSpectrum", tileShape); ArrayColumnDesc<float> wsdesc("WEIGHT_SPECTRUM", "weight per corr/chan", @@ -355,7 +398,7 @@ namespace LOFAR { TiledColumnStMan tsmc("CorrectedData", tileShape); makeArrayColumn(tdesc["CORRECTED_DATA"], dataShape, &tsmc, itsMS); - + IPosition iwShape(1, dataShape[1]); IPosition iwShapeTile(2, tileShape[1], tileShape[2]); TiledColumnStMan tsmw("TiledImagingWeight", iwShapeTile); @@ -508,7 +551,28 @@ namespace LOFAR { ArrayColumn<Complex> dataCol(out, itsDataColName); ArrayColumn<Bool> flagCol(out, "FLAG"); ScalarColumn<Bool> flagRowCol(out, "FLAG_ROW"); - dataCol.putColumn (buf.getData()); + + if (buf.getData().empty()) { + return; + } + + // If compressing, flagged values need to be set to NaN to decrease the dynamic range + if(itsStManKeys.stManName == "dysco") + { + casa::Cube<casa::Complex> dataCopy = buf.getData().copy(); + casa::Cube<casa::Complex>::iterator dataIter = dataCopy.begin(); + for(casa::Cube<bool>::const_iterator flagIter = buf.getFlags().begin(); flagIter != buf.getFlags().end(); ++flagIter) + { + if(*flagIter) + *dataIter = casa::Complex(std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN()); + ++dataIter; + } + dataCol.putColumn (dataCopy); + } + else { + dataCol.putColumn (buf.getData()); + } + flagCol.putColumn (buf.getFlags()); // A row is flagged if no flags in the row are False. Vector<Bool> rowFlags (partialNFalse(buf.getFlags(), IPosition(2,0,1)) == 0u); diff --git a/CEP/DP3/DPPP/src/NDPPP.cc b/CEP/DP3/DPPP/src/NDPPP.cc index 5652c6c38ab579ea15edaa698ecc3352d20e74be..f668199eedb7ac631606550e7ec487c99c0f9331 100644 --- a/CEP/DP3/DPPP/src/NDPPP.cc +++ b/CEP/DP3/DPPP/src/NDPPP.cc @@ -68,11 +68,12 @@ int main(int argc, char *argv[]) return 0; } - string parsetName(""); - if (argc > 1 && string(argv[1]).find('=')==string::npos) { + string parsetName; + if (argc > 1 && string(argv[1]).find('=') == string::npos) { // First argument is parset name (except if it's a key-value pair) parsetName = argv[1]; - } else if (argc==1) { // If no arguments given: try to load [N]DPPP.parset + } else if (argc==1) { + // No arguments given: try to load [N]DPPP.parset if (casa::File("NDPPP.parset").exists()) { parsetName="NDPPP.parset"; } else if (casa::File("DPPP.parset").exists()) { diff --git a/CEP/DP3/DPPP/src/Predict.cc b/CEP/DP3/DPPP/src/Predict.cc index 64db694225cb1c64de98ff83ee4ae1bf083777fd..e8b318a82fcb41b337322ef9db88ad4bb3c3caa3 100644 --- a/CEP/DP3/DPPP/src/Predict.cc +++ b/CEP/DP3/DPPP/src/Predict.cc @@ -337,9 +337,12 @@ namespace LOFAR { MDirection::J2000); StationResponse::vector3r_t srcdir = dir2Itrf(dir,itsMeasConverters[thread]); - ApplyBeam::applyBeam(info(), time, data0, srcdir, refdir, + float* dummyweight = 0; + + ApplyBeam::applyBeam(info(), time, data0, dummyweight, srcdir, refdir, tiledir, itsAntBeamInfo[thread], - itsBeamValues[thread], itsUseChannelFreq, false, itsBeamMode); + itsBeamValues[thread], itsUseChannelFreq, false, + itsBeamMode, false); //Add temporary buffer to itsModelVis std::transform(itsModelVis[thread].data(), diff --git a/CEP/DP3/DPPP/src/StefCal.cc b/CEP/DP3/DPPP/src/StefCal.cc index d23a73f3bf28f50856be1e68ba145bdb45840243..a9e2f62410309854a5333fa0e46e9de4d73747a4 100644 --- a/CEP/DP3/DPPP/src/StefCal.cc +++ b/CEP/DP3/DPPP/src/StefCal.cc @@ -40,53 +40,46 @@ namespace LOFAR { double tolerance, uint maxAntennas, bool detectStalling, uint debugLevel) : _nSt (maxAntennas), + _badIters (0), + _veryBadIters (0), _solInt (solInt), _nChan (nChan), _mode (mode), _tolerance (tolerance), + _totalWeight (0.), _detectStalling (detectStalling), _debugLevel (debugLevel) { - _antMap.resize(maxAntennas, -1); - resetVis(maxAntennas); + resetVis(); + _nSt = maxAntennas; if (_mode=="fulljones") { _nCr=4; _nSp=1; _savedNCr=4; - } else if (_mode=="scalarphase") { + } else if (_mode=="scalarphase" || _mode=="tec" || _mode=="scalaramplitude" + || _mode=="tecandphase") { _nCr=1; _nSp=2; _savedNCr=1; - } else { // mode=="phaseonly", mode=="diagonal" + } else { // mode=="phaseonly", mode=="diagonal", mode=="amplitudeonly" + ASSERT (_mode=="phaseonly" || _mode=="diagonal" || _mode=="amplitudeonly"); _nCr=1; _nSp=1; _savedNCr=2; } - init(); - } + _vis.resize(IPosition(6,_nSt,2,_solInt,_nChan,2,_nSt)); + _mvis.resize(IPosition(6,_nSt,2,_solInt,_nChan,2,_nSt)); - void StefCal::resetVis(uint nSt) { - _nSt = nSt; - _vis.resize(IPosition(6,nSt,2,_solInt,_nChan,2,nSt)); - _mvis.resize(IPosition(6,nSt,2,_solInt,_nChan,2,nSt)); - _vis=0; - _mvis=0; - - if (_mode=="fulljones" || _mode=="scalarphase") { + if (_mode=="fulljones" || _mode=="scalarphase" || _mode=="tec" || _mode=="scalaramplitude" + || _mode=="tecandphase") { _nUn = _nSt; } else { + ASSERT (_mode=="phaseonly" || _mode=="diagonal" || _mode=="amplitudeonly"); _nUn = 2*_nSt; } - } - - void StefCal::init() { - _dg=1.0e29; - _dgx=1.0e30; - _dgs.clear(); - _g.resize(_nUn,_nCr); _gold.resize(_nUn,_nCr); _gx.resize(_nUn,_nCr); @@ -94,48 +87,138 @@ namespace LOFAR { _h.resize(_nUn,_nCr); _z.resize(_nUn*_nChan*_solInt*_nSp,_nCr); - // Initialize all vectors - double fronormvis=0; - double fronormmod=0; + _stationFlagged.resize(_nSt, false); - DComplex* t_vis_p=_vis.data(); - DComplex* t_mvis_p=_mvis.data(); + init(true); + } - uint vissize=_vis.size(); - for (uint i=0;i<vissize;++i) { - fronormvis+=norm(t_vis_p[i]); - fronormmod+=norm(t_mvis_p[i]); - } + void StefCal::resetVis() { + _vis=0; + _mvis=0; + _totalWeight = 0.; + } + + void StefCal::clearStationFlagged() { + _stationFlagged=false; + } - fronormvis=sqrt(fronormvis); - fronormmod=sqrt(fronormmod); + void StefCal::init(bool initSolutions) { + _dg = 1.0e29; + _dgx = 1.0e30; + _dgs.clear(); + + _badIters = 0; + _veryBadIters = 0; + + if (initSolutions) { + double ginit=1.0; + if (_mode != "phaseonly" && _mode != "scalarphase" && + _mode != "tec" && _mode != "tecandphase") { + // Initialize solution with sensible amplitudes + double fronormvis=0; + double fronormmod=0; - double ginit=1; - if (_nSt>0 && abs(fronormmod)>1.e-15) { - ginit=sqrt(fronormvis/fronormmod); + DComplex* t_vis_p=_vis.data(); + DComplex* t_mvis_p=_mvis.data(); + + uint vissize=_vis.size(); + for (uint i=0;i<vissize;++i) { + fronormvis+=norm(t_vis_p[i]); + fronormmod+=norm(t_mvis_p[i]); + } + + fronormvis=sqrt(fronormvis); + fronormmod=sqrt(fronormmod); + if (abs(fronormmod)>1.e-15) { + ginit=sqrt(fronormvis/fronormmod); + } else { + ginit=1.0; + } + } + + if (_nCr==4) { + for (uint ant=0;ant<_nUn;++ant) { + _g(ant,0)=ginit; + _g(ant,1)=0.; + _g(ant,2)=0.; + _g(ant,3)=ginit; + } + } else { + _g=ginit; + } + } else { // Take care of NaNs in solution + double ginit=0.; + bool ginitcomputed=false; + for (uint ant=0; ant<_nUn; ++ant) { + if (!isFinite(_g(ant,0).real()) ) { + if (!ginitcomputed && !_stationFlagged[ant%_nSt]) { + // Avoid calling getAverageUnflaggedSolution for stations that are always flagged + ginit = getAverageUnflaggedSolution(); + ginitcomputed = true; + if (ginit==0) { + init(true); + return; + } + } + if (_nCr==4) { + _g(ant,0)=ginit; + _g(ant,1)=0.; + _g(ant,2)=0.; + _g(ant,3)=ginit; + } else { + _g(ant,0)=ginit; + } + } + } } - if (_nCr==4) { - for (uint st=0;st<_nUn;++st) { - _g(st,0)=ginit; - _g(st,1)=0.; - _g(st,2)=0.; - _g(st,3)=ginit; + } + + double StefCal::getAverageUnflaggedSolution() { + // Get average solution of unflagged antennas only once + // Unflagged means unflagged in previous time slot, so + // look at NaNs, don't look at stationFlagged (that's for + // the current timeslot). + double total=0.; + uint unflaggedstations=0; + for (uint ant2=0; ant2<_nUn; ++ant2) { + if (isFinite(_g(ant2,0).real())) { + total += abs(_g(ant2,0)); + unflaggedstations++; + if (_nCr==4) { + total += abs(_g(ant2,3)); + unflaggedstations++; + } } + } + if (unflaggedstations==0) { + return 0.; } else { - _g=ginit; + return total/unflaggedstations; } - - _gx = _g; } StefCal::Status StefCal::doStep(uint iter) { + _gxx = _gx; + _gx = _g; + + bool allFlagged=true; + for (uint st1=0;st1<_nSt;++st1) { + if (!_stationFlagged[st1]) { + allFlagged=false; + break; + } + } + if (allFlagged) { + return CONVERGED; + } + if (_mode=="fulljones") { doStep_polarized(); doStep_polarized(); return relax(2*iter); } else { - doStep_unpolarized(_mode=="phaseonly" || _mode=="scalarphase"); - doStep_unpolarized(_mode=="phaseonly" || _mode=="scalarphase"); + doStep_unpolarized(); + doStep_unpolarized(); return relax(2*iter); } } @@ -151,6 +234,10 @@ namespace LOFAR { } for (uint st1=0;st1<_nSt;++st1) { + if (_stationFlagged[st1]) { + continue; + } + DComplex* vis_p; DComplex* mvis_p; Vector<DComplex> w(_nCr); @@ -206,33 +293,37 @@ namespace LOFAR { } } - void StefCal::doStep_unpolarized(bool phaseOnly) { + void StefCal::doStep_unpolarized() { _gold=_g; - for (uint st=0;st<_nUn;++st) { - _h(st,0)=conj(_g(st,0)); + for (uint ant=0;ant<_nUn;++ant) { + _h(ant,0)=conj(_g(ant,0)); } for (uint st1=0;st1<_nUn;++st1) { + if (_stationFlagged[st1%_nSt]) { + continue; + } DComplex* vis_p; DComplex* mvis_p; double ww=0; // Same as w, but specifically for pol==false DComplex tt=0; // Same as t, but specifically for pol==false - DComplex* z_p=_z.data(); mvis_p=&_mvis(IPosition(6,0,0,0,0,st1/_nSt,st1%_nSt)); vis_p = &_vis(IPosition(6,0,0,0,0,st1/_nSt,st1%_nSt)); for (uint st1pol=0;st1pol<_nSp;++st1pol) { for (uint ch=0;ch<_nChan;++ch) { for (uint time=0;time<_solInt;++time) { - DComplex* h_p=_h.data(); - for (uint st2=0;st2<_nUn;++st2) { - *z_p = h_p[st2] * *mvis_p; //itsMVis(IPosition(6,st2%nSt,st2/nSt,time,ch,st1/nSt,st1%nSt)); - ww+=norm(*z_p); - tt+=conj(*z_p) * *vis_p; //itsVis(IPosition(6,st2%nSt,st2/nSt,time,ch,st1/nSt,st1%nSt)); - mvis_p++; - vis_p++; - z_p++; + for (uint st2pol=0;st2pol<_nSp;++st2pol) { + DComplex* h_p=_h.data(); + for (uint st2=0;st2<_nUn;++st2) { + DComplex z(h_p[st2] * *mvis_p); //itsMVis(IPosition(6,st2%nSt,st2/nSt,time,ch,st1/nSt,st1%nSt)); + ASSERT(isFinite(z)); + ww+=norm(z); + tt+=conj(z) * *vis_p; //itsVis(IPosition(6,st2%nSt,st2/nSt,time,ch,st1/nSt,st1%nSt)); + mvis_p++; + vis_p++; + } } //cout<<"iS.z bij ch="<<ch<<"="<<iS.z<<endl<<"----"<<endl; } @@ -240,40 +331,63 @@ namespace LOFAR { } //cout<<"st1="<<st1%nSt<<(st1>=nSt?"y":"x")<<", t="<<tt<<" "; //cout<<", w="<<ww<<" "; + ASSERT(ww!=0); _g(st1,0)=tt/ww; //cout<<", g="<<iS.g(st1,0)<<endl; - if (phaseOnly) { + if (_mode=="phaseonly" || _mode=="scalarphase" || _mode=="tec" || _mode=="tecandphase") { + ASSERT(abs(_g(st1,0))!=0); _g(st1,0)/=abs(_g(st1,0)); + ASSERT(isFinite(_g(st1,0))); + } else if (_mode=="amplitudeonly" || _mode=="scalaramplitude") { + _g(st1,0) = abs(_g(st1,0)); + } + + if (_debugLevel>2) { + cout<<endl<<"gi=["; + ASSERT(isFinite(_g(0,0))); + for (uint ant=0; ant<_nUn; ++ant) { + if (ant>0) cout<<","; + cout<<_g(ant,0); + } + cout<<"]"<<endl; } } } - casa::Matrix<casa::DComplex> StefCal::getSolution() { - casa::Matrix<casa::DComplex> sol; - sol.resize(_antMap.size(), _savedNCr); + void StefCal::incrementWeight(float weight) { + _totalWeight += weight; + } - uint sSt=0; // Index in stefcal numbering - for (uint st=0; st<_antMap.size(); ++st) { - if (_antMap[st]==-1) { - for (uint cr=0; cr<_nCr; ++cr) { - sol(st,cr)=std::numeric_limits<double>::quiet_NaN(); - } - } else { - for (uint cr=0; cr<_nCr; ++cr) { - sol(st,cr)=_g(sSt,cr); - if (_mode=="diagonal" || _mode=="phaseonly") { - sol(st,cr+1)=_g(sSt+_nSt,cr); - } - if (cr==_nCr-1) { - sSt++; + casa::Matrix<casa::DComplex> StefCal::getSolution(bool setNaNs) { + if (setNaNs && _debugLevel>0) { + cout<<endl<<"dg=["; + for (uint iter=0; iter<_dgs.size(); ++iter) { + if (iter>0) cout<<","; + cout<<_dgs[iter]; + } + cout<<"]"<<endl; + } + + if (_debugLevel>2) { + cout<<endl<<"g=["; + for (uint ant=0; ant<_nUn; ++ant) { + if (ant>0) cout<<","; + cout<<_g(ant,0); + } + cout<<"]"<<endl; + } + + if (setNaNs) { + for (uint ant=0; ant<_nUn; ++ant) { + if (_stationFlagged[ant%_nSt]) { + for (uint cr=0; cr<_nCr; ++cr) { + _g(ant,cr)=std::numeric_limits<double>::quiet_NaN(); } } } } - ASSERT(sSt==_nSt); - - return sol; + return _g; } StefCal::Status StefCal::relax(uint iter) { @@ -292,28 +406,38 @@ namespace LOFAR { double c2 = 1.2; double dgxx; bool threestep = false; - int badIters=0; - int maxBadIters=5; + uint maxBadIters=(_mode=="tec"||_mode=="tecandphase"?2:3); int sstep=0; - if (_detectStalling && iter > 10 && _dgx-_dg <= 5.0e-3*_dg) { - // This iteration did not improve much upon the previous - // Stalling detection only after 20 iterations, to account for - // ''startup problems'' - if (_debugLevel>3) { - cout<<"**"<<endl; + if ((_detectStalling && iter > 3) || ((_mode=="tec"||_mode=="tecandphase") && iter>2)) { + double improvement = _dgx-_dg; + + if (abs(improvement) < 5.0e-2*_dg) { + // This iteration did not improve much upon the previous + // Stalling detection only after 4 iterations, to account for + // ''startup problems'' (not for tec, where stalling happens very soon) + if (_debugLevel>3) { + cout<<"**"<<endl; + } + _badIters++; + } else if (improvement < 0) { + _veryBadIters++; + } else { + _badIters=0; } - badIters++; - } else { - badIters=0; - } - if (badIters>=maxBadIters && _detectStalling) { - if (_debugLevel>3) { - cout<<"Detected stall"<<endl; + if (_badIters>=maxBadIters) { + if (_debugLevel>3) { + cout<<"Detected stall"<<endl; + } + return STALLED; + } else if (_veryBadIters > maxBadIters) { + if (_debugLevel>3) { + cout<<"Detected fail"<<endl; + } + return FAILED; } - return STALLED; } dgxx = _dgx; @@ -332,7 +456,7 @@ namespace LOFAR { fronormg=sqrt(fronormg); _dg = fronormdiff/fronormg; - if (_debugLevel>1) { + if (_debugLevel>0) { _dgs.push_back(_dg); } @@ -400,9 +524,6 @@ namespace LOFAR { sstep = sstep - 1; } } - _gxx = _gx; - _gx = _g; - return NOTCONVERGED; } } //# end namespace diff --git a/CEP/DP3/DPPP/src/phasefitter.cc b/CEP/DP3/DPPP/src/phasefitter.cc new file mode 100644 index 0000000000000000000000000000000000000000..2391ada19965fd23ca752007fd09718170ade858 --- /dev/null +++ b/CEP/DP3/DPPP/src/phasefitter.cc @@ -0,0 +1,215 @@ +//# phasefitter.cc: Class to perform phase fitting (TEC), allowing phase wraps +//# Copyright (C) 2016 +//# ASTRON (Netherlands Institute for Radio Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +//# +//# This file is part of the LOFAR software suite. +//# The LOFAR software suite is free software: you can redistribute it and/or +//# modify it under the terms of the GNU General Public License as published +//# by the Free Software Foundation, either version 3 of the License, or +//# (at your option) any later version. +//# +//# The LOFAR software suite is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License along +//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +//# +//# $Id: phasefitter.cc 21598 2012-07-16 08:07:34Z offringa $ +//# +//# @author Andre Offringa + +#include <lofar_config.h> +#include "DPPP/phasefitter.h" + +#include <limits> + +double PhaseFitter::TEC2ModelCost(double alpha, double beta) const +{ + double costVal = 0.0; + for(int i=0; i!=Size(); ++i) { + double estphase = TEC2ModelFuncWrapped(_frequencies[i], alpha, beta); + double dCost = fmod(std::fabs(estphase - _phases[i]), 2.0*M_PI); + if(dCost > M_PI) dCost = 2.0*M_PI - dCost; + dCost *= _weights[i]; + costVal += dCost; + } + return costVal; +} + +double PhaseFitter::fitTEC2ModelBeta(double alpha, double betaEstimate) const { + double weightSum = 0.0; + for(size_t iter=0; iter!=3; ++iter) { + double sum = 0.0; + for(size_t i=0; i!=Size(); ++i) { + double p = _phases[i], e = TEC2ModelFunc(_frequencies[i], alpha, betaEstimate); + double dist = fmod(p - e, 2.0*M_PI); + if(dist < -M_PI) + dist += 2.0*M_PI; + else if(dist > M_PI) + dist -= 2.0*M_PI; + sum += dist * _weights[i]; + weightSum += _weights[i]; + } + betaEstimate = betaEstimate + sum / weightSum; + } + return fmod(betaEstimate, 2.0*M_PI); +} + +void PhaseFitter::bruteForceSearchTEC2Model(double& lowerAlpha, double& upperAlpha, double& beta) const +{ + double minCost = std::numeric_limits<double>::max(); + double alphaOversampling = 256; + //size_t betaOversampling = 16; + double dAlpha = upperAlpha - lowerAlpha; + int alphaIndex = 0; + for(int i=0; i!=alphaOversampling; ++i) { + // make r between [0, 1] + double r = double(i)/alphaOversampling; + double alpha = lowerAlpha + r*dAlpha; + double curBeta = fitTEC2ModelBeta(alpha, beta); + double costVal = TEC2ModelCost(alpha, curBeta); + if(costVal < minCost) { + beta = curBeta; + minCost = costVal; + alphaIndex = i; + } + } + double newLowerAlpha = double(alphaIndex-1)/alphaOversampling*dAlpha + lowerAlpha; + upperAlpha = double(alphaIndex+1)/alphaOversampling*dAlpha + lowerAlpha; + lowerAlpha = newLowerAlpha; +} + +double PhaseFitter::ternarySearchTEC2ModelAlpha(double startAlpha, double endAlpha, double& beta) const +{ + size_t iter = 0; + double dCost, lAlpha, rAlpha; + do { + lAlpha = startAlpha + (endAlpha - startAlpha) * (1.0/3.0); + rAlpha = startAlpha + (endAlpha - startAlpha) * (2.0/3.0); + double lBeta = fitTEC2ModelBeta(lAlpha, beta); + double rBeta = fitTEC2ModelBeta(rAlpha, beta); + double lCost = TEC2ModelCost(lAlpha, lBeta); + double rCost = TEC2ModelCost(rAlpha, rBeta); + if(lCost < rCost) { + endAlpha = rAlpha; + beta = lBeta; + } else { + startAlpha = lAlpha; + beta = rBeta; + } + dCost = std::fabs(lCost - rCost); + ++iter; + } while(dCost > _fittingAccuracy && iter < 100); + double finalAlpha = (lAlpha + rAlpha) * 0.5; + beta = fitTEC2ModelBeta(finalAlpha, beta); + return finalAlpha; +} + +void PhaseFitter::fillDataWithTEC2Model(double alpha, double beta) +{ + for(size_t ch=0; ch!=Size(); ++ch) + _phases[ch] = TEC2ModelFunc(_frequencies[ch], alpha, beta); +} + +void PhaseFitter::fillDataWithTEC1Model(double alpha) +{ + for(size_t ch=0; ch!=Size(); ++ch) + _phases[ch] = TEC1ModelFuncWrapped(_frequencies[ch], alpha); +} + +void PhaseFitter::FitTEC2ModelParameters(double& alpha, double& beta) const +{ + double lowerAlpha = -40000.0e6, upperAlpha = 40000.0e6; + bruteForceSearchTEC2Model(lowerAlpha, upperAlpha, beta); + alpha = (lowerAlpha + upperAlpha) * 0.5; + //beta = fitBeta(alpha, beta); + alpha = ternarySearchTEC2ModelAlpha(lowerAlpha, upperAlpha, beta); +} + +double PhaseFitter::FitDataToTEC2Model(double& alpha, double& beta) +{ + FitTEC2ModelParameters(alpha, beta); + fillDataWithTEC2Model(alpha, beta); + return TEC2ModelCost(alpha, beta); +} + +double PhaseFitter::FitDataToTEC1Model(double& alpha) +{ + FitTEC1ModelParameters(alpha); + fillDataWithTEC1Model(alpha); + return TEC1ModelCost(alpha); +} + +void PhaseFitter::FitTEC1ModelParameters(double& alpha) const +{ + double lowerAlpha = -40000.0e6, upperAlpha = 40000.0e6; + bruteForceSearchTEC1Model(lowerAlpha, upperAlpha); + alpha = ternarySearchTEC1ModelAlpha(lowerAlpha, upperAlpha); +} + +#include <iostream> +void PhaseFitter::bruteForceSearchTEC1Model(double& lowerAlpha, double& upperAlpha) const +{ + double minCost = std::numeric_limits<double>::max(); + double alphaOversampling = 256; + double dAlpha = upperAlpha - lowerAlpha; + int alphaIndex = 0; + for(int i=0; i!=alphaOversampling; ++i) { + // make r between [0, 1] + double r = double(i)/alphaOversampling; + double alpha = lowerAlpha + r*dAlpha; + // We have to have some freedom in the fit to make sure + // we do rule out an area with an unwripping that is correct + // Hence we use the two-parameter model and allow beta to be fitted. + // The ternary search will fix alpha to accomodate a zero beta. + double curBeta = fitTEC2ModelBeta(alpha, 0.0); + double costVal = TEC2ModelCost(alpha, curBeta); + if(costVal < minCost) { + minCost = costVal; + alphaIndex = i; + } + } + double newLowerAlpha = double(alphaIndex-1)/alphaOversampling*dAlpha + lowerAlpha; + upperAlpha = double(alphaIndex+1)/alphaOversampling*dAlpha + lowerAlpha; + lowerAlpha = newLowerAlpha; + //std::cout << "alpha in " << lowerAlpha << "-" << upperAlpha << '\n'; +} + +double PhaseFitter::TEC1ModelCost(double alpha) const +{ + double costVal = 0.0; + for(int i=0; i!=Size(); ++i) { + double estphase = TEC1ModelFuncWrapped(_frequencies[i], alpha); + double dCost = fmod(std::fabs(estphase - _phases[i]), 2.0*M_PI); + if(dCost > M_PI) dCost = 2.0*M_PI - dCost; + dCost *= _weights[i]; + costVal += dCost; + } + return costVal; +} + +double PhaseFitter::ternarySearchTEC1ModelAlpha(double startAlpha, double endAlpha) const +{ + size_t iter = 0; + double dCost, lAlpha, rAlpha; + do { + lAlpha = startAlpha + (endAlpha - startAlpha) * (1.0/3.0); + rAlpha = startAlpha + (endAlpha - startAlpha) * (2.0/3.0); + double lCost = TEC1ModelCost(lAlpha); + double rCost = TEC1ModelCost(rAlpha); + if(lCost < rCost) { + endAlpha = rAlpha; + } else { + startAlpha = lAlpha; + } + dCost = std::fabs(lCost - rCost); + ++iter; + //std::cout << iter << '\t' << startAlpha << '\t' << endAlpha << '\n'; + } while(dCost > _fittingAccuracy && iter < 100); + double finalAlpha = (lAlpha + rAlpha) * 0.5; + return finalAlpha; +} + diff --git a/CEP/DP3/DPPP/test/CMakeLists.txt b/CEP/DP3/DPPP/test/CMakeLists.txt index cbe865863114480aee462f556f65e35f893ab1b0..f5e4caa66ae1360515f5433759d5c13d8e31e830 100644 --- a/CEP/DP3/DPPP/test/CMakeLists.txt +++ b/CEP/DP3/DPPP/test/CMakeLists.txt @@ -17,6 +17,7 @@ lofar_add_test(tPhaseShift tPhaseShift.cc) lofar_add_test(tStationAdder tStationAdder.cc) lofar_add_test(tScaleData tScaleData.cc) lofar_add_test(tApplyCal tApplyCal.cc) +lofar_add_test(tApplyCal2) lofar_add_test(tFilter tFilter.cc) #lofar_add_test(tDemixer tDemixer.cc) lofar_add_test(tNDPPP tNDPPP.cc) diff --git a/CEP/DP3/DPPP/test/tApplyBeam.run b/CEP/DP3/DPPP/test/tApplyBeam.run index 8878b0ad9ecf1ceadeba6111c041649ff1f5756e..19daa40a2d1c7d8f867db1d1b3b2147645bc14dc 100755 --- a/CEP/DP3/DPPP/test/tApplyBeam.run +++ b/CEP/DP3/DPPP/test/tApplyBeam.run @@ -58,3 +58,9 @@ echo; echo "Test with beammode=ELEMENT"; echo # Compare the DATA column of the output MS with the BBS reference output. $taqlexe 'select from outinv.ms t1, tApplyBeam.tab t2 where not all(near(t1.DATA,t2.DATA_ELEMENT,1e-5) || (isnan(t1.DATA) && isnan(t2.DATA_ELEMENT)))' > taql.out diff taql.out taql.ref || exit 1 + +echo; echo "Test with updateweights=true"; echo +../../src/NDPPP msin=tNDPPP-generic.MS msout=. steps=[applybeam] applybeam.updateweights=truue msout.weightcolumn=NEW_WEIGHT_SPECTRUM +# Check that the weights have changed +$taqlexe 'select from tNDPPP-generic.MS where all(near(WEIGHT_SPECTRUM, NEW_WEIGHT_SPECTRUM))' > taql.out +diff taql.out taql.ref || exit 1 diff --git a/CEP/DP3/DPPP/test/tApplyCal2.run b/CEP/DP3/DPPP/test/tApplyCal2.run new file mode 100755 index 0000000000000000000000000000000000000000..9aa6f4c40327e7bb8aedeb5f0c93568de77bfa38 --- /dev/null +++ b/CEP/DP3/DPPP/test/tApplyCal2.run @@ -0,0 +1,99 @@ +#!/bin/bash + +# Get the taql executable and srcdir (script created by cmake's CONFIGURE_FILE). +source findenv.run_script +echo "srcdirx=$rt_srcdir" + +# Set srcdir if not defined (in case run by hand). +if test "$srcdir" = ""; then + srcdir="$rt_srcdir" +fi + +if test ! -f ${srcdir}/tNDPPP-generic.in_MS.tgz; then + exit 3 # untested +fi + +set -e # Stop on any error + +rm -rf tApplyCal2_tmp +mkdir -p tApplyCal2_tmp +# Unpack the MS and other files and do the DPPP run. +cd tApplyCal2_tmp +tar zxf ${srcdir}/tNDPPP-generic.in_MS.tgz + +# Create expected taql output. +echo " select result of 0 rows" > taql.ref + +echo; echo "Creating parmdb with defvalues 3" +../../../../ParmDB/src/parmdbm <<EOL +open table="tApplyCal.parmdb" +adddef Gain:0:0:Real values=3. +adddef Gain:1:1:Real values=3. +EOL + +echo; echo "Testing without updateweights" +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false +$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out +diff taql.out taql.ref || exit 1 +$taqlexe 'select from tNDPPP-generic.MS where not(all(WEIGHTS_NEW~=WEIGHT_SPECTRUM))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Testing with updateweights" +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 msout.weightcolumn=WEIGHTS_NEW steps=[applycal] applycal.parmdb=tApplyCal.parmdb showcounts=false applycal.updateweights=true +$taqlexe 'select from tNDPPP-generic.MS where not(all(WEIGHTS_NEW~=81*WEIGHT_SPECTRUM))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Testing CommonScalarPhase" +rm -rf tApplyCal.parmdb +../../../../ParmDB/src/parmdbm <<EOL +open table="tApplyCal.parmdb" +adddef CommonScalarPhase values=0 +EOL +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false +$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Testing ScalarPhase" +rm -rf tApplyCal.parmdb +../../../../ParmDB/src/parmdbm <<EOL +open table="tApplyCal.parmdb" +adddef ScalarPhase values=0 +EOL +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false +$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Testing ScalarPhase values" +rm -rf tApplyCal.parmdb +../../../../ParmDB/src/parmdbm <<EOL +open table="tApplyCal.parmdb" +add ScalarPhase:CS001HBA0 values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:CS002HBA0 values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:CS002HBA1 values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:CS004HBA1 values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:RS106HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:RS208HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:RS305HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarPhase:RS307HBA values=[0.,0.,0.,0.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +EOL +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=commonscalarphase showcounts=false +$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=DATA3))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Testing ScalarAmplitude values" +rm -rf tApplyCal.parmdb +../../../../ParmDB/src/parmdbm <<EOL +open table="tApplyCal.parmdb" +add ScalarAmplitude:CS001HBA0 values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:CS002HBA0 values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:CS002HBA1 values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:CS004HBA1 values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:RS106HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:RS208HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:RS305HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +add ScalarAmplitude:RS307HBA values=[3.,3.,3.,3.], domain=[10e6, 200e6, 4472025735, 4972025795], shape=[2,2], shape=[2,2] +EOL +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DATA3 steps=[applycal] applycal.parmdb=tApplyCal.parmdb applycal.correction=scalaramplitude showcounts=false +$taqlexe 'select from tNDPPP-generic.MS where not(all(DATA~=9*DATA3))' > taql.out +diff taql.out taql.ref || exit 1 + diff --git a/CEP/DP3/DPPP/test/tApplyCal2.sh b/CEP/DP3/DPPP/test/tApplyCal2.sh new file mode 100755 index 0000000000000000000000000000000000000000..2c166e188f8c8671ea007c9e084af4ce70fde811 --- /dev/null +++ b/CEP/DP3/DPPP/test/tApplyCal2.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runctest.sh tApplyCal2 diff --git a/CEP/DP3/DPPP/test/tAverager.cc b/CEP/DP3/DPPP/test/tAverager.cc index 62e18028951974ec754272e95ef0858f3dd9e9e0..fa8d6856e6b6699929f97d32aa1d1cf9ac7db6b7 100644 --- a/CEP/DP3/DPPP/test/tAverager.cc +++ b/CEP/DP3/DPPP/test/tAverager.cc @@ -30,6 +30,8 @@ #include <casa/Arrays/ArrayMath.h> #include <casa/Arrays/ArrayLogical.h> #include <casa/Arrays/ArrayIO.h> + +#include <casa/Quanta/Quantum.h> #include <iostream> using namespace LOFAR; @@ -471,6 +473,37 @@ void test1(int ntime, int nbl, int nchan, int ncorr, execute (step1); } +// Like test 1, but specify target resolution +void test1resolution(int ntime, int nbl, int nchan, int ncorr, + double timeresolution, double freqresolution, + string frequnit, bool flag) +{ + cout << "test1: ntime=" << ntime << " nrbl=" << nbl << " nchan=" << nchan + << " ncorr=" << ncorr << " timeresolution=" << timeresolution + << " freqresolution=" << freqresolution << endl; + // Create the steps. + TestInput* in = new TestInput(ntime, nbl, nchan, ncorr, flag); + DPStep::ShPtr step1(in); + ParameterSet parset; + parset.add ("freqresolution", toString(freqresolution)+frequnit); + parset.add ("timeresolution", toString(timeresolution)); + DPStep::ShPtr step2(new Averager(in, parset, "")); + + if (!frequnit.empty()) { + Quantity q(freqresolution, frequnit); + freqresolution = q.getValue("Hz", true); + } + + int navgchan = std::max(1, int(freqresolution / 100000 + 0.5)); + int navgtime = std::max(1, int(timeresolution / 5. + 0.5)); + + DPStep::ShPtr step3(new TestOutput(ntime, nbl, nchan, ncorr, + navgtime, navgchan, flag)); + step1->setNextStep (step2); + step2->setNextStep (step3); + execute (step1); +} + // Like test1, but the averaging is done in two steps. void test2(int ntime, int nbl, int nchan, int ncorr, bool flag) { @@ -572,6 +605,10 @@ int main() test1(11, 3, 30, 2, 3, 3, false); test1(10, 3, 32, 4, 1, 32, false); test1(10, 3, 32, 1, 1, 1, false); + + test1resolution(10, 3, 32, 4, 10., 100000, "Hz", false); + test1resolution(11, 3, 32, 4, 1., 800, "kHz", false); + test1resolution(11, 3, 32, 4, 15., 0.4, "MHz", false); test2(10, 3, 32, 2, true); test2(10, 3, 32, 2, false); test3(1, 1); diff --git a/CEP/DP3/DPPP/test/tGainCal.run b/CEP/DP3/DPPP/test/tGainCal.run index a92ab4621b1e3758553fd0d4f890131170f70703..ea76e4fe3716239e1bf0df89697fcd641eacd59b 100755 --- a/CEP/DP3/DPPP/test/tGainCal.run +++ b/CEP/DP3/DPPP/test/tGainCal.run @@ -13,6 +13,8 @@ if test ! -f ${srcdir}/tNDPPP-generic.in_MS.tgz; then exit 3 # untested fi +set -e # Stop on any error + rm -rf tGainCal_tmp mkdir -p tGainCal_tmp # Unpack the MS and other files and do the DPPP run. @@ -23,30 +25,86 @@ tar zxf ${srcdir}/tGainCal.tab.tgz # Create expected taql output. echo " select result of 0 rows" > taql.ref -# Create MODEL_DATA so that residual can be computed +echo "Creating MODEL_DATA so that residual can be computed" ../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=MODEL_DATA steps=[predict] predict.sourcedb=tNDPPP-generic.MS/sky predict.usebeammodel=false - echo; echo "Test caltype=diagonal"; echo -../../src/NDPPP msin=tNDPPP-generic.MS msout= steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.solint=4 +../../src/NDPPP msin=tNDPPP-generic.MS msout= steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.propagatesolutions=true gaincal.solint=1 + ../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL steps=[applycal] applycal.parmdb=tNDPPP-generic.MS/inst-diagonal -# Compare the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima. -$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_DIAGONAL-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_DIAGONAL-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where not near(dpppres,bbsres,1.e-2)' > taql.out +echo "Comparing the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima." +$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_DIAGONAL-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_DIAGONAL-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where dpppres>bbsres*1.02' > taql.out +diff taql.out taql.ref || exit 1 +echo "Checking that not everything was flagged" +$taqlexe 'select from tNDPPP-generic.MS where all(FLAG) groupby true having gcount()>100' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Test caltype=diagonal with timeslotsperparmupdate=4"; echo +../../src/NDPPP msin=tNDPPP-generic.MS msout= steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal-tpp gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.solint=4 gaincal.timeslotsperparmupdate=1 gaincal.propagatesolutions=false +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL_TPP steps=[applycal] applycal.parmdb=tNDPPP-generic.MS/inst-diagonal-tpp +$taqlexe 'select from tNDPPP-generic.MS where not all(near(DPPP_DIAGONAL, DPPP_DIAGONAL_TPP))' + +echo "Comparing the difference between applying with timeslotsperparmupdate = default and timeslotsperparmupdate=1" +$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_DIAGONAL-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_DIAGONAL-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where dpppres>bbsres*1.02' > taql.out +diff taql.out taql.ref || exit 1 +echo "Checking that not everything was flagged" +$taqlexe 'select from tNDPPP-generic.MS where all(FLAG) groupby true having gcount()>100' > taql.out diff taql.out taql.ref || exit 1 echo; echo "Test caltype=fulljones"; echo -../../src/NDPPP msin=tNDPPP-generic.MS msout= steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-fulljones gaincal.usebeammodel=false gaincal.caltype=fulljones gaincal.solint=1 +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_FULLJONES_GAINCAL steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-fulljones gaincal.usebeammodel=false gaincal.caltype=fulljones gaincal.solint=1 gaincal.applysolution=true ../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_FULLJONES steps=[applycal] applycal.parmdb=tNDPPP-generic.MS/inst-fulljones -# Compare the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima. -$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_FULLJONES-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_FULLJONES-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where not near(dpppres,bbsres,1.e-2)' > taql.out +echo "Comparing the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima." +$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_FULLJONES-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_FULLJONES-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where dpppres>bbsres*1.02' > taql.out +diff taql.out taql.ref || exit 1 +echo "Comparing the solutions from gaincal + applycal with gaincal directly" +$taqlexe 'select from tNDPPP-generic.MS where not(all(DPPP_FULLJONES ~= DPPP_FULLJONES))' > taql.out +diff taql.out taql.ref || exit 1 +echo "Checking that not everything was flagged" +$taqlexe 'select from tNDPPP-generic.MS where all(FLAG) groupby true having gcount()>100' > taql.out diff taql.out taql.ref || exit 1 echo; echo "Test caltype=diagonal, nchan=2"; echo -../../src/NDPPP msin=tNDPPP-generic.MS msout= steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal-nchan gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.solint=4 gaincal.nchan=2 +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL_NCHAN_GAINCAL steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal-nchan gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.solint=4 gaincal.nchan=2 gaincal.applysolution=true ../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL_NCHAN steps=[applycal] applycal.parmdb=tNDPPP-generic.MS/inst-diagonal-nchan -# Compare the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima. -$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_DIAGONAL_NCHAN-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_DIAGONAL_NCHAN-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where not near(dpppres,bbsres,1.e-2)' > taql.out +echo "Comparing the bbs residual with the dppp residual (solutions will not be equal, but residual should be equal). This avoids issues with local minima." +$taqlexe 'select from (select gsumsqr(sumsqr(abs(iif(t1.FLAG,0,t1.DPPP_DIAGONAL_NCHAN-t1.MODEL_DATA)))) as dpppres, gsumsqr(sumsqr(abs(iif(FLAG,0,t2.BBS_DIAGONAL_NCHAN-t1.MODEL_DATA)))) as bbsres from tNDPPP-generic.MS t1, tGainCal.tab t2) where dpppres>bbsres*1.02' > taql.out diff taql.out taql.ref || exit 1 + +echo "Comparing the solutions from gaincal + applycal with gaincal directly" +$taqlexe 'select from tNDPPP-generic.MS where not(all(DPPP_DIAGONAL_NCHAN_GAINCAL ~= DPPP_DIAGONAL_NCHAN))' > taql.out +diff taql.out taql.ref || exit 1 + +echo "Checking that not everything was flagged" +$taqlexe 'select from tNDPPP-generic.MS where all(FLAG) groupby true having gcount()>100' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Test caltype=diagonal, nchan=2, solint=7"; echo +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL_NCHAN_7_GAINCAL steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-diagonal-nchan gaincal.usebeammodel=false gaincal.caltype=diagonal gaincal.solint=4 gaincal.nchan=2 gaincal.applysolution=true +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_DIAGONAL_NCHAN_7 steps=[applycal] applycal.parmdb=tNDPPP-generic.MS/inst-diagonal-nchan + +echo "Comparing the solutions from gaincal + applycal with gaincal directly" +$taqlexe 'select from tNDPPP-generic.MS where not(all(DPPP_DIAGONAL_NCHAN_7_GAINCAL ~= DPPP_DIAGONAL_NCHAN_7))' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Test caltype=tec"; echo +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_TEC steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-tec gaincal.caltype=tec gaincal.solint=2 +# For now, only testing that the right parameter names are in the output +echo " select result of 1 rows" > taql1.ref +$taqlexe 'select from tNDPPP-generic.MS/inst-tec where (select NAME from ::NAMES)[NAMEID]=="TEC:CS001HBA0"' > taql.out +diff taql.out taql1.ref || exit 1 +$taqlexe 'select from tNDPPP-generic.MS/inst-tec where (select NAME from ::NAMES)[NAMEID]=="CommonScalarPhase:CS001HBA0"' > taql.out +diff taql.out taql.ref || exit 1 + +echo; echo "Test caltype=tecandphase"; echo +../../src/NDPPP msin=tNDPPP-generic.MS msout=. msout.datacolumn=DPPP_TEC steps=[gaincal] gaincal.sourcedb=tNDPPP-generic.MS/sky gaincal.parmdb=tNDPPP-generic.MS/inst-tecandphase gaincal.caltype=tecandphase gaincal.solint=2 +# For now, only testing that the right parameter names are in the output +echo " select result of 1 rows" > taql1.ref +$taqlexe 'select from tNDPPP-generic.MS/inst-tecandphase where (select NAME from ::NAMES)[NAMEID]=="TEC:CS001HBA0"' > taql.out +diff taql.out taql1.ref || exit 1 +$taqlexe 'select from tNDPPP-generic.MS/inst-tecandphase where (select NAME from ::NAMES)[NAMEID]=="CommonScalarPhase:CS001HBA0"' > taql.out +diff taql.out taql1.ref || exit 1 + diff --git a/CEP/Imager/LofarFT/src/LofarConvolutionFunction.cc b/CEP/Imager/LofarFT/src/LofarConvolutionFunction.cc index 4b04e7d1fc35941e52fc3d863db04a031de3182e..2c78fcf65847abec9c051dbe7a5f84c0636c932c 100644 --- a/CEP/Imager/LofarFT/src/LofarConvolutionFunction.cc +++ b/CEP/Imager/LofarFT/src/LofarConvolutionFunction.cc @@ -1165,7 +1165,7 @@ namespace LOFAR IPosition posOut(4,1,1,1,1); IPosition posEl(2,1,1); Complex pix; - uInt jjpix,ch; + uInt jjpix,ch = 0; /* gcc somehow complains ch can be uninitialised */ Complex ElementValue; for(ch=0; ch<input_grid.shape()[3]; ++ch){ #pragma omp parallel diff --git a/CEP/MS/include/MS/BaselineSelect.h b/CEP/MS/include/MS/BaselineSelect.h index 80daa0578712324f07b0451ef34a4a6329b7a42b..540ab928f70f1cd81a05cad41cb1045d653770cb 100644 --- a/CEP/MS/include/MS/BaselineSelect.h +++ b/CEP/MS/include/MS/BaselineSelect.h @@ -29,11 +29,18 @@ //# Includes #include <Common/lofar_string.h> +#include <ms/MSSel/MSSelectionErrorHandler.h> +#include <ostream> + //# Forward Declarations namespace casa { template<class T> class Matrix; + template<class T> class Vector; + class Table; + class TableExprNode; + class MPosition; } namespace LOFAR @@ -51,10 +58,48 @@ class BaselineSelect public: // Parse the MSSelection baseline string and create a Matrix telling // which baselines are selected. + // Possible messages from the parser are written to the ostream. static casa::Matrix<bool> convert (const string& msName, - const string& baselineSelection); + const string& baselineSelection, + std::ostream&); + + // Parse the MSSelection baseline string and create a Matrix telling + // which baselines are selected. + // The input is a vector of station names and positions. + // Possible messages from the parser are written to the ostream. + static casa::Matrix<bool> convert (const casa::Vector<casa::String>& names, + const std::vector<casa::MPosition>& pos, + const casa::Vector<casa::Int>& ant1, + const casa::Vector<casa::Int>& ant2, + const string& baselineSelection, + std::ostream&); + +private: + static casa::Matrix<bool> convert (casa::Table& anttab, + casa::TableExprNode& a1, + casa::TableExprNode& a2, + const string& baselineSelection, + std::ostream& os); + }; + + +// This class handles an error from the Casacore's MSAntennaParse. +// It adds the message to the message list of the parent BaselineSelect. +class BaselineSelectErrorHandler : public casa::MSSelectionErrorHandler +{ +public: + BaselineSelectErrorHandler (std::ostream& os) + : itsStream (os) + {} + virtual ~BaselineSelectErrorHandler(); + virtual void reportError (const char *token, const casa::String message); +private: + std::ostream& itsStream; +}; + + // @} } // end namespace diff --git a/CEP/MS/include/MS/MSCreate.h b/CEP/MS/include/MS/MSCreate.h index e91a1f665e5b18b1a6168ca7f08ebd864aeec1b0..f35ce3dda2a2a84b4b7a93b323155aa0fa47b402 100644 --- a/CEP/MS/include/MS/MSCreate.h +++ b/CEP/MS/include/MS/MSCreate.h @@ -84,6 +84,10 @@ public: // Destructor ~MSCreate(); + // Close all subtables to limit the nr of open files. + // This can be done once all subtables have been written. + void closeSubTables(); + // Add the extra columns needed for lwimager for every column not existing. // These are CORRECTED_DATA, MODEL_DATA, and IMAGING_WEIGHT. // Furthermore it sets the CHANNEL_SELECTION keyword for casa::VisSet. diff --git a/CEP/MS/src/BaselineSelect.cc b/CEP/MS/src/BaselineSelect.cc index 8015a4de6833abf2149d2ada2c0f5c70da7dd16f..123085347349b3b7387646dddd6837dc8bed9207 100644 --- a/CEP/MS/src/BaselineSelect.cc +++ b/CEP/MS/src/BaselineSelect.cc @@ -22,23 +22,38 @@ //# Includes #include <MS/BaselineSelect.h> +#include <Common/LofarLogger.h> + #include <ms/MeasurementSets/MeasurementSet.h> +#include <ms/MeasurementSets/MSAntenna.h> +#include <ms/MeasurementSets/MSAntennaColumns.h> #if defined(casacore) #include <ms/MSSel/MSSelection.h> +#include <ms/MSSel/MSAntennaParse.h> +#include <ms/MSSel/MSAntennaGram.h> #else #include <ms/MeasurementSets/MSSelection.h> +#include <ms/MeasurementSets/MSAntennaParse.h> +#include <ms/MeasurementSets/MSAntennaGram.h> #endif +#include <tables/Tables/Table.h> +#include <tables/Tables/SetupNewTab.h> +#include <tables/Tables/TableRecord.h> #include <tables/Tables/TableParse.h> #include <tables/Tables/ScalarColumn.h> +#include <tables/Tables/ScaColDesc.h> +#include <measures/Measures/MPosition.h> #include <casa/Arrays/Matrix.h> #include <casa/Arrays/Vector.h> + using namespace casa; namespace LOFAR { Matrix<bool> BaselineSelect::convert (const string& msName, - const string& baselineSelection) + const string& baselineSelection, + std::ostream& os) { // Find the unique baselines in the MS. // Do not use unique sort, because that is slow for a large MS. @@ -60,23 +75,91 @@ namespace LOFAR { } bltab = tab(Vector<uInt>(rows)); } - MeasurementSet ms(bltab); - MSSelection select; - // Set given selection strings. - select.setAntennaExpr (baselineSelection); - // Create a table expression over a MS representing the selection - TableExprNode node = select.toTableExprNode (&ms); - Table seltab = ms(node); - // Get the antenna numbers. - Vector<Int> a1 = ROScalarColumn<Int>(seltab, "ANTENNA1").getColumn(); - Vector<Int> a2 = ROScalarColumn<Int>(seltab, "ANTENNA2").getColumn(); - int nant = ms.antenna().nrow(); - Matrix<bool> bl(nant, nant, false); - for (uint i=0; i<a1.size(); ++i) { - bl(a1[i], a2[i]) = true; - bl(a2[i], a1[i]) = true; + TableExprNode a1 (bltab.col("ANTENNA1")); + TableExprNode a2 (bltab.col("ANTENNA2")); + Table anttab (bltab.keywordSet().asTable("ANTENNA")); + return convert (anttab, a1, a2, baselineSelection, os); + } + + casa::Matrix<bool> BaselineSelect::convert (const Vector<String>& names, + const vector<MPosition>& pos, + const Vector<Int>& ant1, + const Vector<Int>& ant2, + const string& baselineSelection, + std::ostream& os) + { + ASSERT (names.size() == pos.size()); + // Create a temporary MSAntenna table in memory for parsing purposes. + SetupNewTable antNew(String(), MSAntenna::requiredTableDesc(), + Table::New); + Table anttab(antNew, Table::Memory, names.size()); + MSAntenna msant(anttab); + MSAntennaColumns antcol(msant); + antcol.name().putColumn (names); + for (size_t i=0; i<pos.size(); ++i) { + antcol.positionMeas().put (i, pos[i]); } - return bl; + // Create a temporary table holding the antenna numbers of the baselines. + TableDesc td; + td.addColumn (ScalarColumnDesc<Int>("ANTENNA1")); + td.addColumn (ScalarColumnDesc<Int>("ANTENNA2")); + SetupNewTable tabNew(String(), td, Table::New); + Table tab(tabNew, Table::Memory, ant1.size()); + ScalarColumn<Int> ac1(tab, "ANTENNA1"); + ScalarColumn<Int> ac2(tab, "ANTENNA2"); + ac1.putColumn (ant1); + ac2.putColumn (ant2); + // Do the selection using the temporary tables. + TableExprNode a1 (tab.col("ANTENNA1")); + TableExprNode a2 (tab.col("ANTENNA2")); + return convert (anttab, a1, a2, baselineSelection, os); + } + + Matrix<bool> BaselineSelect::convert (Table& anttab, + TableExprNode& a1, + TableExprNode& a2, + const string& baselineSelection, + std::ostream& os) + { + // Overwrite the error handler to ignore errors for unknown antennas. + // First construct MSSelection, because it resets the error handler. + Vector<Int> selectedAnts1; + Vector<Int> selectedAnts2; + Matrix<Int> selectedBaselines; + MSSelectionErrorHandler* curHandler = MSAntennaParse::thisMSAErrorHandler; + BaselineSelectErrorHandler errorHandler (os); + MSAntennaParse::thisMSAErrorHandler = &errorHandler; + try { + // Create a table expression representing the selection. + TableExprNode node = msAntennaGramParseCommand + (anttab, a1, a2, baselineSelection, + selectedAnts1, selectedAnts2, selectedBaselines); + // Get the antenna numbers. + Table seltab = node.table()(node); + Vector<Int> a1 = ROScalarColumn<Int>(seltab, "ANTENNA1").getColumn(); + Vector<Int> a2 = ROScalarColumn<Int>(seltab, "ANTENNA2").getColumn(); + int nant = anttab.nrow(); + Matrix<bool> bl(nant, nant, false); + for (uint i=0; i<a1.size(); ++i) { + bl(a1[i], a2[i]) = true; + bl(a2[i], a1[i]) = true; + } + MSAntennaParse::thisMSAErrorHandler = curHandler; + return bl; + } catch (const std::exception&) { + MSAntennaParse::thisMSAErrorHandler = curHandler; + throw; + } + } + + + BaselineSelectErrorHandler::~BaselineSelectErrorHandler() + {} + + void BaselineSelectErrorHandler::reportError (const char* token, + const String message) + { + itsStream << message << token << endl; } } // end namespace diff --git a/CEP/MS/src/MSCreate.cc b/CEP/MS/src/MSCreate.cc index 95028ba30e66aabdd35f9bb4234a76398300fb42..b0aca89eb9f578a725f076e6101b6456a0e51a54 100644 --- a/CEP/MS/src/MSCreate.cc +++ b/CEP/MS/src/MSCreate.cc @@ -263,6 +263,28 @@ void MSCreate::createMS (const String& msName, } } +void MSCreate::closeSubTables() +{ + itsMS->antenna() = MSAntenna(); + itsMS->dataDescription() = MSDataDescription(); + itsMS->doppler() = MSDoppler(); + itsMS->feed() = MSFeed(); + itsMS->field() = MSField(); + itsMS->flagCmd() = MSFlagCmd(); + itsMS->freqOffset() = MSFreqOffset(); + itsMS->history() = MSHistory(); + itsMS->observation() = MSObservation(); + itsMS->pointing() = MSPointing(); + itsMS->polarization() = MSPolarization(); + itsMS->processor() = MSProcessor(); + itsMS->source() = MSSource(); + itsMS->spectralWindow() = MSSpectralWindow(); + itsMS->state() = MSState(); + itsMS->sysCal() = MSSysCal(); + itsMS->weather() = MSWeather(); + itsMS->keywordSet().closeTables(); +} + int MSCreate::addBand (int npolarizations, int nchannels, double refFreq, double chanWidth) { @@ -305,7 +327,7 @@ int MSCreate::addBand (int npolarizations, int nchannels, if (polnr < 0) { polnr = addPolarization (npolarizations); } - // Add a row to the DATADESCRIPTION subtable. + // Add a row to the DATA_DESCRIPTION subtable. MSDataDescription msdd = itsMS->dataDescription(); MSDataDescColumns msddCol(msdd); uInt rownr = msdd.nrow(); @@ -313,7 +335,7 @@ int MSCreate::addBand (int npolarizations, int nchannels, msddCol.spectralWindowId().put (rownr, rownr); msddCol.polarizationId().put (rownr, polnr); msddCol.flagRow().put (rownr, False); - // Add a row to the SPECTRALWINDOW subtable. + // Add a row to the SPECTRAL_WINDOW subtable. // Find the total bandwidth from the minimum and maximum. Vector<double> stFreqs = chanFreqs - chanWidths/2.; Vector<double> endFreqs = chanFreqs + chanWidths/2.; @@ -588,7 +610,7 @@ void MSCreate::updateTimes() Double midTime = (itsStartTime + endTime) / 2; // Update all rows in FEED subtable. { - MSFeed mssub = itsMS->feed(); + MSFeed mssub (itsMS->keywordSet().asTable("FEED")); MSFeedColumns mssubCol(mssub); Vector<Double> val(mssub.nrow()); val = midTime; @@ -598,7 +620,7 @@ void MSCreate::updateTimes() } // Update all rows in POINTING subtable. { - MSPointing mssub = itsMS->pointing(); + MSPointing mssub (itsMS->keywordSet().asTable("POINTING")); MSPointingColumns mssubCol(mssub); Vector<Double> val(mssub.nrow()); val = midTime; @@ -608,7 +630,7 @@ void MSCreate::updateTimes() } // Update all rows in OBSERVATION subtable. { - MSObservation msobs = itsMS->observation(); + MSObservation msobs (itsMS->keywordSet().asTable("OBSERVATION")); MSObservationColumns msobsCol(msobs); Vector<Double> timeRange(2); timeRange(0) = itsStartTime; @@ -843,6 +865,10 @@ void MSCreate::addImagerColumns (MeasurementSet& ms) ms.addColumn (td, stMan); // Set MODEL_DATA keyword for casa::VisSet. // Sort out the channel selection. + if (ms.spectralWindow().isNull()) { + ms.spectralWindow() = + MSSpectralWindow(ms.keywordSet().asTable("SPECTRAL_WINDOW")); + } MSSpWindowColumns msSpW(ms.spectralWindow()); Matrix<Int> selection(2, msSpW.nrow()); // Fill in default selection (all bands and channels). diff --git a/CEP/MS/src/makems.cc b/CEP/MS/src/makems.cc index 7f9b48afa3ca57e900483a36b53e98a53531dad3..2989ace240fe35eea19779a9839105edd49a99a8 100644 --- a/CEP/MS/src/makems.cc +++ b/CEP/MS/src/makems.cc @@ -37,6 +37,7 @@ #include <casa/Arrays/Matrix.h> #include <casa/Arrays/Vector.h> #include <casa/OS/Path.h> +#include <casa/OS/Timer.h> #include <tables/Tables/Table.h> #include <tables/Tables/ScalarColumn.h> #include <tables/Tables/ArrayColumn.h> @@ -173,6 +174,7 @@ void readParms (const string& parset) void createMS (int nband, int bandnr, const string& msName) { + Timer timer; int nfpb = itsNFreq/itsNBand; MSCreate msmaker(msName, itsStartTime, itsStepTime, nfpb, itsNCorr, itsAntPos, itsAntennaTableName, itsWriteAutoCorr, @@ -187,6 +189,9 @@ void createMS (int nband, int bandnr, const string& msName) for (uint i=0; i<itsRa.size(); ++i) { msmaker.addField (itsRa[i], itsDec[i]); } + // Close all subtables to reduce nr of open files. + msmaker.closeSubTables(); + timer.show ("Create MS"); for (int i=0; i<itsNTime; ++i) { msmaker.writeTimeStep(); } diff --git a/CEP/MS/src/makems.cfg b/CEP/MS/src/makems.cfg index 7330fa8aae890e24002ca7ea7539ff5b8608f926..8cd40b02cf9ad8850e8d9f5d416b78fb5c9cd11d 100644 --- a/CEP/MS/src/makems.cfg +++ b/CEP/MS/src/makems.cfg @@ -7,13 +7,13 @@ Declination=62.34.44.313606568 NBands=4 NFrequencies=64 NTimes=14 -NParts=2 +NParts=4 Dirs=["node1:abc", 'node1:def'] TileSizeFreq=8 TileSizeRest=10 WriteAutoCorr=T -AntennaTableName=~/WSRT_ANTENNA -MSName=tst/test.MS +AntennaTableName=~/data/WSRT_ANTENNA +MSName=test.MS VDSPath=. FlagColumn=LOFAR_FLAG NFlagBits=8 diff --git a/CEP/MS/src/msoverview.cc b/CEP/MS/src/msoverview.cc index d4d110fdf011724b8183bd6a360df4d34c3d8cd0..8d2e3df428d3e08352743a599318025330e5f269 100644 --- a/CEP/MS/src/msoverview.cc +++ b/CEP/MS/src/msoverview.cc @@ -112,7 +112,8 @@ int main (int argc, char* argv[]) logio << LogIO::NORMAL << endl << LogIO::POST; summ.listField (logio, False); logio << LogIO::NORMAL << endl << LogIO::POST; - summ.listSpectralAndPolInfo (logio, verbose); + ///summ.listSpectralAndPolInfo (logio, verbose, False); // new version + summ.listSpectralAndPolInfo (logio, verbose); // old version if (verbose) { logio << LogIO::NORMAL << endl << LogIO::POST; summ.listAntenna (logio, True); diff --git a/CEP/MS/test/tBaselineSelect.cc b/CEP/MS/test/tBaselineSelect.cc index b335d4696139c19e8c76cf23fe0e984edfe62389..29e6089085bf40b18cdf668304571d19b138382e 100644 --- a/CEP/MS/test/tBaselineSelect.cc +++ b/CEP/MS/test/tBaselineSelect.cc @@ -22,21 +22,60 @@ //# Includes #include <MS/BaselineSelect.h> +#include <Common/LofarLogger.h> + +#include <measures/Measures/MPosition.h> +#include <measures/Measures/MeasTable.h> #include <casa/Arrays/ArrayIO.h> +#include <casa/BasicSL/STLIO.h> #include <iostream> using namespace LOFAR; using namespace casa; using namespace std; +void testTemp() +{ + Vector<String> names(5); + names[0] = "CS013HBA0"; + names[1] = "CS013HBA1"; + names[2] = "CS014HBA0"; + names[3] = "RS015"; + names[4] = "DE013"; + Vector<Int> a1(15); + Vector<Int> a2(15); + int inx=0; + for (int i=0; i<5; ++i) { + for (int j=i; j<5; ++j) { + a1[inx] = i; + a2[inx] = j; + inx++; + } + } + vector<MPosition> pos(5); + ASSERT (MeasTable::Observatory (pos[0], "WSRT")); + ASSERT (MeasTable::Observatory (pos[1], "WSRT")); + ASSERT (MeasTable::Observatory (pos[2], "LOFAR")); + ASSERT (MeasTable::Observatory (pos[3], "WSRT")); + ASSERT (MeasTable::Observatory (pos[4], "LOFAR")); + // Try a few selection strings. + cout << BaselineSelect::convert (names, pos, a1, a2, "CS013HBA[01]", cout); + cout << BaselineSelect::convert (names, pos, a1, a2, "CS013HBA[23]", cout); + cout << BaselineSelect::convert (names, pos, a1, a2, "CS*&&[CR]S*", cout); + cout << BaselineSelect::convert (names, pos, a1, a2, "<10000", cout); +} + int main(int argc, char* argv[]) { try { - if (argc < 3) { + if (argc == 1) { + testTemp(); + } else if (argc < 3) { cout << "Run as: tBaselineSelect msname expr" << endl; return 0; + } else { + cout << BaselineSelect::convert (argv[1], argv[2], cout); } - cout << BaselineSelect::convert (argv[1], argv[2]); } catch (exception& x) { cout << "Unexpected expection: " << x.what() << endl; return 1; diff --git a/CEP/MS/test/tBaselineSelect.sh b/CEP/MS/test/tBaselineSelect.sh new file mode 100755 index 0000000000000000000000000000000000000000..7b258eed4f26146ba36fcf023c24b9f29e53f421 --- /dev/null +++ b/CEP/MS/test/tBaselineSelect.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runctest.sh tBaselineSelect diff --git a/CEP/MS/test/tBaselineSelect.stdout b/CEP/MS/test/tBaselineSelect.stdout new file mode 100644 index 0000000000000000000000000000000000000000..1db78f080d6ea409c85ecf008b5c76774762cd35 --- /dev/null +++ b/CEP/MS/test/tBaselineSelect.stdout @@ -0,0 +1,26 @@ +Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) +[0, 1, 1, 1, 1 + 1, 0, 1, 1, 1 + 1, 1, 0, 0, 0 + 1, 1, 0, 0, 0 + 1, 1, 0, 0, 0] +Antenna Expression: No match found for token(s) "CS013HBA[23]" +No match found for the antenna specificion [ID(s): []] +Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) +[0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0] +Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) +[1, 1, 1, 1, 0 + 1, 1, 1, 1, 0 + 1, 1, 1, 1, 0 + 1, 1, 1, 0, 0 + 0, 0, 0, 0, 0] +Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) +[1, 1, 0, 1, 0 + 1, 1, 0, 1, 0 + 0, 0, 1, 0, 1 + 1, 1, 0, 1, 0 + 0, 0, 1, 0, 1] diff --git a/CEP/Pipeline/docs/genericpipeline/gendocrestruct.rst b/CEP/Pipeline/docs/genericpipeline/gendocrestruct.rst new file mode 100644 index 0000000000000000000000000000000000000000..4b196b054eb98a25917fa18a728071031537e092 --- /dev/null +++ b/CEP/Pipeline/docs/genericpipeline/gendocrestruct.rst @@ -0,0 +1,822 @@ + +Generic Pipeline +================ + +This Chapter will explain how to define a pipeline for the execution with the Lofar Pipeline Framework. For this purpose a Generic Pipeline has been created which can be configured to integrate user programs in a flexible way. + +Introduction +------------ +The Pipeline Framework is used for automated processing on the CEP cluster systems for example for the MSSS pipelines. Writing these pipelines is complicated and requires a lot of knowledge of the framework itself and some programming skills. + +The generic pipeline is a pipeline based on that system. It helps users with the design and execution of their own workflow without the need to understand the underlying system. For a pipeline the user should organize the work into building blocks. Typically you want to use existing blocks that do standard operations like Calibrate-Stand-Alone or DPPP or the AWImager. Work that has to be done in preparation or post processing for these operations can and should be implemented as additional steps for the pipeline. + +The advantage is that the user does not need to manage any kind of multiprocessing for data parallel operations. Input and output filenames of steps are mostly hidden and can be managed in a consistent way. Parameters can be reduced to a minimum with different sets of default parsets for every step. Integration of other peoples steps and reusing existing work are one of the primary goals of the generic pipeline. A users pipeline can run on a single workstation and in a cluster environment without the need to change the pipeline parset or to program process management. + +In an ideal case there are enough predefined possible steps for the user to choose from so that defining a pipeline comes down configuring a minimal set of program arguments. + +Steps and arguments to the steps are defined in a parset file. This parset is then the argument for the ``genericpipeline.py``. The pipelines name will be the first part of the parsets name ``mypipeline.parset``. Log files will be tracked under that name and have to be deleted along with the working directory when the pipeline should be restartet from the beginning. + +Usage +----- +The generic pipeline is part of the Lofar Software Package and is basically a runnable python script. The pipeline definition is written in a parset file. After you have loaded the Lofar environment it can be run with the command: +:: + + > genericpipeline.py <parset-file> [options] + +Where possible options are ``-d`` for debug logging and ``-c <config-file>`` to use a specific pipeline configuration file. + +Quick Start +----------- + +Before you can use the genericpipeline you need to customize the configuration +file: + + * Copy ``$LOFARROOT/share/pipeline/pipeline.cfg`` to someplace in your ``$HOME`` and open it in an editor. + * It starts with a section **[DEFAULT]**, in there you need to edit two entries: + + #. **runtime\_directory**: This is the directory where the pipeline puts logfiles, parsets, the status of successful steps etc. Set this to a directory in your ``$HOME``, e.g. ``/home/<username>/PipelineExample/runtime`` + #. **working\_directory**: This is the directory where the processed data of the intermediate and final steps is put. Set this to a directory on your data disk, e.g. ``/data/scratch/<username>/PipelineExample`` + + * In case you do not run it on a cluster, you need to tell the pipeline to start the processes on the local machine: + + #. Search for the section **[cluster]**, set the entry **clusterdesc** to: ``(lofarroot)s/share/local.clusterdesc`` + #. Add a section ``[remote]`` by adding the following lines: + :: + + [remote] + method = local + max_per_node = <NumCPUCores> + + If there is already another section **[remote]**, then remove that. + + * The pipeline framework contains a number of parallel processing schemes for working on multi-node clusters. Ask you local sysadmin for advice. + +Once the pipeline is configured you need a pipeline-parset. A simple example +would be: +:: + + + # Pipeline for running NDPPP on all files in a directory + + #variable parameters + #path to the directory where we are looking for the input data + ! input_path = /data/scratch/dummyuser/test-in + # path to the parset + ! ndppp_parset = /home/dummyuser/parsets/NDPPP-preproc-parset.proto + + pipeline.steps=[createmap,ndppp] + + #Step 1: search for all measurement sets in one directory and generate a mapfile + createmap.control.kind = plugin + createmap.control.type = addMapfile + createmap.control.cmdline.create = mapfile_from_folder + createmap.control.mapfile_dir = input.output.mapfile_dir #this is the name that the mapfile will have + createmap.control.filename = input_data.mapfile + createmap.control.folder = {{ input_path }} #this references to the path we defined above + + #Step 2: run NDPPP with a given parset on all files that the previous step found + ndppp.control.type = dppp + ndppp.control.parset = {{ ndppp_parset }} #this references to the parset we defined above + ndppp.control.max_per_node = 4 #run 4 instances of NDPPP in parallel + ndppp.control.environment = {OMP_NUM_THREADS: 6} #tell NDPPP to use only 6 threads + ndppp.argument.msin = createmap.output.mapfile + + +Pipeline configuration +---------------------- + +The configuration of the Pipeline Framework is mainly done in the two files ``pipeline.cfg`` and ``tasks.cfg``. The ``pipeline.cfg`` contains general settings like job and working directories and where your pipeline installation and task configuration resides. The ``tasks.cfg`` contains the steps that can be used in a pipeline. If you want to use your own configuration the ``pipeline.cfg`` should be an argument to the pipeline call. + +Copy the file ``pipeline.cfg`` from your installation. In a Lofar Framework installation the file can be found in ``LOFARROOT/share/pipeline/pipeline.cfg``. In this file you can configure the basic setup of the Pipeline Framework. You might want to modify the ``runtime_directory`` and the ``working_directory`` especially if you are not the only one using that machine. Those are a little bit ambiguous but generally the ``runtime_directory`` contains parsets, mapfiles, logfiles automatically generated for running the pipeline system. The ``working_directory`` is where the actual data products are being placed and the directory from where the individual programs are called. So look there for any temporary data you would expect. It is fine to set both parameters to point to the same directory and have everything in one place. + +If you want to add your own operations to the generic pipeline some additional configuration is necessary. In the ``pipeline.cfg`` you have to add to the list of ``task_files`` your own ``tasks.cfg``. See the next section for a description of such a file. In the standard install only standard operations are defined and of course only programs that come with the Lofar Software Stack. + +If you want to run a pipeline on your local machine you need to add the following to the configuration: +:: + + [remote] + method = local + max_per_node = 1 + +The ``max_per_node`` value can be overwritten on a step by step bases and indicates how many subprocesses of that step are started concurrently. This number depends on the workload a specific step puts on the system. + +The more advanced user might need to modify the underlying scripts that execute the job. You can use your own master and node scripts without having access to the install directories of the pipeline. Edit the ``recipe_directories`` in the ``pipeline.cfg`` and point it to the folder where you have your ``master``, ``node`` and ``plugin`` directories. + +Task definitions +---------------- +Possible steps for a pipeline are configured as a task in the ``tasks.cfg`` file. The format is comparable to the python configparser class. That means the name of the task is given in brackets followed by parameters for this task. Example: +:: + + [dppp] + recipe = executable_args + executable = (lofarroot)s/bin/NDPPP + args_format = lofar + outputkey = msout + +This task is now accessible in a parset: +:: + + dppp_step.control.type = dppp + dppp_step.argument.msin... + +The ``recipe`` variable is mandatory and needs to be the name of a master script. Historically there are master and node scripts implementing functionality. The generic pipeline only uses one master script, the ``executable_args``. The old ones are still usable but you will most likely never use them. The ``outputkey = msout`` means that the automatically generated file name from the step will be mapped to the argument ``msout``. So now your dppp step has a default argument ``dppp_step.argument.msout=<inputfile>.dppp_step``. + +Node scripts handle the actual process call. Most of the time the node script version of ``executable_args`` will suffice and does not need to be specified. A second useful node script loads python files and runs their ``main()`` method. Meaning you have to have a function called ``main()``. In this way you can store parameters in the pipeline context and use them in later steps. Fore more information on python plugins please look at the feature section. A minimal task for your own python plugin would look like this: +:: + + [mypythontask] + recipe = executable_args + nodescript = python_plugin + executable =/path/to/my_python_script.py + +Now the task can be used in the parset by simply doing the following: +:: + + my_python_step.control.type = mypythontask + my_python_step.argument... + + +Mapfiles +-------- +Mapfiles are the data descriptors of the pipeline framework. Only primitive functions to create and manipulate mapfiles are available at the moment as it is subject of development (see keyword section). + +Contents of a mapfile is the hostname of the machine where your data is, the path to your input data and a field that lets you specify whether to skip the data or not (mark it as bad data). +A mapfile holds entries for all the measurement sets you want to use. + +For every entry in a mapfile a compute job will be created. With the ``max_per_node`` parameter in the step configuration you can specify how many of those should run at the same time on one node. The content of a mapfile is a python dictionary: +:: + + {'host': 'localhost', file: '/path/to/MS', 'skip': False} + +Only primitive functions to create and manipulate mapfiles are available at the moment as it is subject of development. Right now plugins are used (see keyword section on createMapfile plugin) + +Pipeline +-------- +Parset +^^^^^^ +The pipeline itself will be described in a parset. The steps are given as a list. Other general parameters for the pipeline are the optional path to a plugins directory and an existing mapfile. The beginning of a parset looks like this: +:: + + pipeline.steps = [step1,step2,step3,...] + pipeline.pluginpath = plugins # optional + pipeline.mapfile = /path/to/your_data.mapfile # optional + +This steplist determines the order of execution. In what order the steps are implemented in the parset does not matter. However to every rule there is an exception. When using more than one mapfile in a step, the file entries in the first one will be used for the automatic naming scheme. Make sure your first mapfile contains individual names (measurement sets for example). Otherwise you might overwrite your results. + +To prevent the steplist from getting too long you can use sublists like this: + +:: + + pipeline.steps = [step1,substeps,step3,...] + pipeline.steps.substeps = [substep1,substep2,...] + +Steps +^^^^^ +Step names are arbitrary but have to be unique in one pipeline. Step options are organized into two blocks. The first to configure the task and the second to give arguments to program that is called. The keyword for the task options is ``control`` and the step arguments is ``argument``. Any options given here overwrite the ones in the ``tasks.cfg``. If you already have configured all task options in your ``tasks.cfg`` you only need to specify what type of task this step should be. Minimum general usage: +:: + + step.control.type = taskname + step.argument.flags = [command,line,-flags] + +A more specific example: +:: + + step.control.type = dppp + step.control.args_format = lofar + step.control.max_per_node = 4 + step.control.environment = {OMP_NUM_THREADS: 8, SOME_OTHER: env_variable} + step.argument.steps = [c] + step.argument.c.type = gaincal + step.argument.c.usebeammodel = True + . + . + . + +Parameters specified after ``argument`` are passed to the program in a "long option" kind of way. In the example above ``args_format = lofar`` was specified which means the argument will be given as: +:: + + c.type gaincal + +Default is the posix way of giving long options which would look like: +:: + + --c.type=gaincal + +For more option descriptions look at the complete list of keywords for the ``executable_args`` task. There are more things a step could be other than a task. For this the keyword ``step.control.kind`` is used. Please see the "Features" section for details. + +Features +^^^^^^^^ + +**Plugins** + +For quick hacking of functionality plugins can also be used as steps. They are simple python scripts but in contrast to the ``pythontask`` are not tracked by the framework. Also plugins are only executed once and for every entry in a mapfile as this input is not mandatory. Their standard location is in the recipes directory. An extra ``pipelinepath`` can be specified in the pipeline parset. This location will then also be searched when trying to load the plugins. + +Self written plugins must contain the method called ``plugin_main`` which can have a dictionary as return value: +:: + + def plugin_main(args, **kwargs): + result = {} + <some code> + return result + +Plugins were introduced for development purposes and should be removed in the future. But for now they are used to create and manipulate mapfiles. + +**Internal dictionary** + +The pipeline holds an internal results dictionary. Entries are dictionaries themselves. Every step can put his own dictionary in there to pass variables down the pipeline. The default dictionary after starting a pipeline has the same name as the pipeline and has all key value pairs from the ``pipeline.cfg`` as entries. You can also access the following: ``job_dir``, ``parset_dir``, ``mapfile_dir`` and ``mapfile`` if it was given with ``pipeline.mapfile``. + +Normal steps chosen from the tasks list always put a dictionary in the results with their output mapfile. It either has the values of its input or the filenames of the computed output. The name of the dictionary is the steps name. + +A ``python_plugin`` task can write a dictionary to the results dictionary by simply returning an object of that type. + +This output dictionary can be accessed with a special notation ``stepname.output.variable`` useable in any argument. On the left is the stepname of which you want the output from and on the right the name of the variable that was chosen in that steps dictionary. Most of the time you only want to access mapfiles from previous steps. Lets say you created a mapfile in a step called ``createmap``. An example of getting that mapfile in a later step would be: +:: + + mystep.argument.inputfile = createmap.output.mapfile + +Notice that the mapfile represents the input file. If written like this the step will be executed for every "file" entry in the mapfile. + +**String replacement syntax** + +Since you want your step description ordered one after the other but maybe have used arguments that other users need to edit a Replacement Syntax as a convenience feature has been added. This means that you can use placeholders in your step arguments and have the real argument at the top of parset for easier access. The syntax for the placeholder is: +:: + + pipeline.replace.my_awimager = /path/to/my/awimager + . + . + . + myAWImagerStep.control.executable = {{ my_awimager }} + +Also possible is a partial string replacement in the argument like so: +:: + + pipeline.replace.my_awimager_path = /path/to/my/ + . + . + . + myAWImagerStep.control.executable = {{ my_awimager_path }}awimager + +Notice the trailing slash on the path and no space after the curly brackets. The space inside the brackets around the variable is necessary. +An alternative syntax to define the replacement string is to use an exclamation mark like in the popular python package jinja2: +:: + + ! my_awimager = /path/to/my/awimager + +This is just a different way of writing. The first version is more in line with the usual way of writing parsets. + +It is also possible to use environment variables in the replacement value (from version 2.18 onwards). +:: + + ! my_awimager = $HOME/local/bin/awimager + +**Python Plugins** + +A way of using your own scripts within the pipeline framework is taking advantage of the python plugin mechanism. The script only needs to have a function called ``main()``. Any arguments you define in the step have to be handled as arguments of this main function. Lets look at an example where we define a step to be a python plugin: +:: + + toystep.control.type = pythonplugin + toystep.control.executable = /path/to/my/script.py + toystep.argument.flags = previousstep.output.previousstep.mapfile # a positional argument with output filenames from a previous step + toystep.argument.optional = 6 # some more arguments + toystep.argument.threshold = 4 # we want to have in the script + +Now the script would look something like this: +:: + + def main(positional, optional=0, passthrough=0): + outdict = {} + print 'File name: ', str(positional) + + # cast to types is a good idea/needed because parsets only work on strings + derivedval = int(optional) / 2 + + # names in outdict get saved as 'optionalhalf.mapfile' and 'threspix.mapfile' + outdict['optionalhalf'] = derivedval + outdict['threshold'] = passthrough + + return outdict + +You can of course compute values depending on the input data. The different results will be saved in the output mapfiles and are associated to the data in this way for later use. A next step might use your output like this: +:: + + nextstep.control.type = another_task + nextstep.argument.needed_computed_value = toystep.output.optionalhalf.mapfile + nextstep.argument.some_other_value = toystep.output.threshold.mapfile + +This enables you to write simple scripts which can be tested on a single measurement set. Within the pipeline framework the script can then operate on the whole observation. + +**Loops** + +It is possible to loop steps. A loop is a different kind of step and contains a list of steps: +:: + + loopme.control.kind = loop + loopme.control.type = conditional + loopme.control.loopcount = [int] + loopme.control.loopsteps = [loopstep1,loopstep2,loopstep3,...] + +The steps will be looped until one of the steps puts in its output dictionary a ``'break': True`` or until the loop counter reaches the specified integer ``loopcount``. So maybe as last step of your list inside the loop you want a step that checks a break condition and outputs its value. + +**Subpipelines** + +A subpipeline is like the loop another kind of step. This construct is for the case that you have a working set of steps and do not want to clutter your parset or want to have a structure with individual parsets for individual functionality. You can hand over mapfiles to a subpipeline and access it with the output keyword mechanism ``subpipeline_name.output.mapfile``. Arguments you give to the subpipeline will be handled as string replacements (see paragraph above). This enables you to write parsets that can be run individually or as subpipelines without any change to the parset. + +The steps that will be added from the subpipeline will be prefixed with the subpipeline name. This makes it possible to have step definitions with the same name in the master parset and in the subpipeline parset without creating any conflicts. The following would be an example of a subpipeline step and the beginning of the subpipeline. The step: +:: + + subpipe.control.kind = pipeline + subpipe.control.type = my_subpipeline.parset + subpipe.control.mapfile_in = some_previous_step.output.mapfile + + +Syntax and Keywords +^^^^^^^^^^^^^^^^^^^ +ToDo: longish tables of possible keywords and their explanation for createMapfile plugin, executable\_args task, subpipeline controls etc. + +List of possible pipeline parameters + ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|Parameter |Type |Description | ++=========================+=============+=======================================================================================================+ +|**Pipeline** | | | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|pipeline.steps |list |The list of steps the pipeline will execute | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|pipeline.steps.<sublist> |list |This list will replace the <sublist> in the steplist defined in the argument above. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|pipeline.pluginpath |string |(optional) The folder that contains additional plugins. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|pipeline.mapfile |string |(optional) An existing mapfile that is then available in the input dictionary | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|pipeline.replace.<arg> |string |(optional) Will search the rest of the parset for {{ <arg> }} and will replace it with the value | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +| | |You can also use the jinja2 style with an exclamation mark (! <arg>) | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +| | |The value may contain environment variables that will be parsed from the system environment. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|**Steps** | | | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.type |string |What task should be executed by this step. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.<arg> | |Any control argument to configure the chosen type of step. | +| | |For executable_args see the list below. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.argument.flags |list |This optional list will be passed as command line arguments to the executable. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.argument.parset |string |You can specifiy a parset file here with parameters. Usually specified in tasks or the control block. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.argument.<arg> | |Any long option argument for the executable. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|**Loop Step** | |**<step>.control.kind = loop** | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.loopsteps |list |The list of steps for this loop. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.loopcount |int |Number of times the loop will run. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|**Subpipeline** | |**<step>.control.kind = pipeline** | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.type | |The parset of the pipeline to run. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.mapfile_in|string |If you want to start the subpipeline with a specific mapfile. | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.argument.<arg> | |The <arg> and <value> of the arguments will be used in the subpipeline string replacement mechanism | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|**Plugins** | |**<step>.control.kind = plugin** | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.arguments |list |List of arguments that will be passed as args to the plugin | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ +|<step>.control.<arg> | |Arguments that will passed as \*\*kwargs to the plugin | ++-------------------------+-------------+-------------------------------------------------------------------------------------------------------+ + +Defining a task with the master script ``executable_args``. The following are the possible control keys (<step>.control.<parameter>) + ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|Parameter |Type | Default |Description | ++======================+=============+===============+=======================================================================================+ +|**Mandatory** | | | | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|[<task_name>] |string | |The header that gives the step its name (the type value of the step in the parset). | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|recipe |string | |The master script we want to call. Needs to be ``executable_args`` | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|executable |string | |Path to the program that this task will execute. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|**Optional** | | | | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|nodescript |string |executable_args| | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|parsetasfile |boolean |False |Should the arguments of the step be passed as a parset file to the program? | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|nthreads |int |8 |Short argument for setting OMP_NUM_THREADS as environment variable. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|parset |string | |Path to a parset that contains the arguments for this task. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|arguments |list | |List of arguments for the task. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|mapfile_in |string | |The mapfile for this task. Usually contains the input files for the program | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|mapfiles_in |list | |A list of mapfiles if you have multiple sources of input for this task. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|inputkey |string | |The key in the arguments that gets the entries from the mapfile as value. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|inputkeys |string list | |The keys for multiple input mapfiles. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|mapfile_out |string | |Create your own mapfile with outputnames if you want tospecify them yourself. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|mapfiles_out |list | |Same as above for multiple outputs (imagers for example) | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|outputkey |string | |The key in the arguments that gets the entries from the output mapfile as value. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|outputkeys |string list | |The keys for multiple output mapfiles. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|skip_infile |boolean |False |Do not pass the input files to the program. Execute it for every entry in the mapfile | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|skip_outfile |boolean |False |Do not produce output files. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|inplace |boolean |False |Use input names for the output names. Manipulate files inplace. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|outputsuffixes |list | |List of suffixes that the program adds to the output filename (useful for imagers). | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|parsetasfile |boolean |False |Parset given to node scripts is a positional argument or content are named arguments. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|stepname |string | |For custom nameing the result. Default is the stepname from the pipeline parset. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|max_per_node |int | |How many times should this program run on one node. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|environment |dict | |Add environment variables formatted as a python dictionary (number of threads for ex.).| ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ +|error_tolerance |boolean |True |Controls if the program exits on the first error or continues with succeeded MS. | ++----------------------+-------------+---------------+---------------------------------------------------------------------------------------+ + +List of available tasks + ++----------------------+-------------+-------------+---------------------------------------------------------------------------------------+ +|Task | ++----------------------+-------------+-------------+---------------------------------------------------------------------------------------+ +|Parameter |Type | Default |Description | ++======================+=============+=============+=======================================================================================+ +|recipe |string | |Option only used in the tasks.cfg for defining the master recipe to use for the task. | ++----------------------+-------------+-------------+---------------------------------------------------------------------------------------+ +|kind |string |recipe |Tell the pipeline the kind of step [recipe, plugin, loop, pipeline]. | ++----------------------+-------------+-------------+---------------------------------------------------------------------------------------+ +|type |string | |If kind is recipe specify which task from the tasks.cfg are we going to use | ++----------------------+-------------+-------------+---------------------------------------------------------------------------------------+ + + +Cookbook example +^^^^^^^^^^^^^^^^ + +This is a pipeline version of the Lofar Imaging Cookbook practical example from chapter thirteen: +:: + + # Pipeline that runs the processing steps from the LOFAR Cookbook tutorial. + + pipeline.steps=[finddata, flag_compress, calibrate1, flagstation, calibrate2, image3c295, make_concatmap, combineMS, reCalibrate, imageCombined, subtract3C295, copySubtracted, parmDBmap, applycal, imageField] + + #variable parameters + pipeline.replace.input_path = /globaldata/COOKBOOK/Tutorial/3c295/data/L74759/ + pipeline.replace.flag_compress_parset = /globaldata/COOKBOOK/Tutorial/3c295/parsets/hba/NDPPP_HBA_preprocess.parset + pipeline.replace.bbs_calibration_parset = /globaldata/COOKBOOK/Tutorial/3c295/parsets/hba/bbs.parset + pipeline.replace.calibrator_skymodel = /globaldata/COOKBOOK/Tutorial/3c295/models/3C295TWO.skymodel + pipeline.replace.bbs_subtraction_parset = /globaldata/COOKBOOK/Tutorial/3c295/parsets/hba/bbs_subtract3c295.parset + pipeline.replace.bbs_correct_parset = /globaldata/COOKBOOK/Tutorial/3c295/parsets/hba/bbs_correct3c295.parset + + # 1st step + # find the data that we want to process and create a mapfile + finddata.control.kind = plugin # plugin -> short, non-parallel step + finddata.control.type = createMapfile # generate a new mapfile + finddata.control.method = mapfile_from_folder # look for all files in a given directory + finddata.control.folder = {{ input_path }} # directory in which to look for the data + finddata.control.mapfile_dir = input.output.mapfile_dir # put the mapfile into the runtime directory + finddata.control.filename = finddata.mapfile # name of the generated mapfile + finddata.control.pattern = L*_uv.MS # use only files that match this pattern + + # 2nd step: + # run NDPPP on all files, this will generate new files with the compressed data + flag_compress.control.type = dppp # run ndppp + flag_compress.control.parset = {{ flag_compress_parset }} # name of the parset to use + flag_compress.control.max_per_node = 4 # run at most 4 NDPPP processes per node + flag_compress.control.environment = {OMP_NUM_THREADS: 8} # tell NDPPP to use at most 8 threads per process + flag_compress.argument.msin = finddata.output.mapfile # process the data in the mapfile that was produced in the finddata step + + # 3rd step + # calibrate the data with BBS + calibrate1.control.type = python-calibrate-stand-alone # run BBS on single files + calibrate1.control.max_per_node = 20 # run at most 20 BBS processes per node + calibrate1.argument.force = True # force replaceing of parmDB and skyDB + calibrate1.argument.observation = flag_compress.output.mapfile # run on files generated by flag_compress step + calibrate1.argument.parset = {{ bbs_calibration_parset }} # which parset to use (path specified above) + calibrate1.argument.catalog = {{ calibrator_skymodel }} # which skymodel to use + + # 4th step: + # run NDPPP + # This time without an external parset, specifying everything in here. + flagstation.control.type = dppp # run ndppp + flagstation.control.max_per_node = 4 # run at most 4 NDPPP processes per node + flagstation.control.environment = {OMP_NUM_THREADS: 8} # tell NDPPP to use at most 8 threads per process + flagstation.control.outputkey = # no "outputkey" -> don't generate new outputfiles + flagstation.argument.msin = flag_compress.output.mapfile # run on files generated by flag_compress step + flagstation.argument.msout = . # set msout to "." in parset -> update input file + flagstation.argument.steps = [flag] + flagstation.argument.flag.type = preflagger + flagstation.argument.flag.baseline = [[RS508HBA,RS509HBA],[RS208HBA,RS509HBA],[CS302HBA*]] + + # 5th step + # re-calibrate the data with BBS, (exactly like step 3) + calibrate2.control.type = python-calibrate-stand-alone # run BBS on single files + calibrate2.control.max_per_node = 20 # run at most 20 BBS processes per node + calibrate2.argument.force = True # force replaceing of parmDB and skyDB + calibrate2.argument.observation = flag_compress.output.mapfile # run on files generated by flag_compress step + calibrate2.argument.parset = {{ bbs_calibration_parset }} # which parset to use (path specified above) + calibrate2.argument.catalog = {{ calibrator_skymodel }} # which skymodel to use + + # 6th step + # make a clean image of the calibrated data with awimager + # this will run one awimager process for each subband (so two for the tutorial data) + image3c295.control.type = awimager # run the awimager + image3c295.control.max_per_node = 2 # run at most 2 awimager processes per node + image3c295.control.environment = {OMP_NUM_THREADS: 10} # maximum number of parallel threads + image3c295.argument.ms = flag_compress.output.mapfile # run on files generated by flag_compress step + image3c295.argument.data = CORRECTED_DATA # read from the CORRECTED_DATA column + image3c295.argument.weight = briggs # further imaging parameters ... + image3c295.argument.robust = 0 + image3c295.argument.npix = 4096 + image3c295.argument.cellsize = 5.5arcsec + image3c295.argument.padding = 1.2 + image3c295.argument.stokes = I + image3c295.argument.operation = mfclark + image3c295.argument.wmax = 20000 + image3c295.argument.UVmin = 0.08 + image3c295.argument.UVmax = 18 + image3c295.argument.niter = 1000 + + # 7th step + # make a mapfile that will allow us to concatenate all data-files into one + make_concatmap.control.kind = plugin # plugin -> short, non-parallel step + make_concatmap.control.type = createMapfile # generate a new mapfile + make_concatmap.control.method = mapfile_all_to_one # put all files into one entry + make_concatmap.control.mapfile_in = flag_compress.output.mapfile # name of the input-mapfile + make_concatmap.control.mapfile_dir = input.output.mapfile_dir # put new mapfile into runtime directory + make_concatmap.control.filename = concat.mapfile # name of the new mapfile + + # 8th step + # now combine the MSs with the help of the mapfile from step 7 + combineMS.control.type = dppp # run ndppp + combineMS.control.max_per_node = 1 # run only one NDPPP process at a time + combineMS.control.environment = {OMP_NUM_THREADS: 10} # tell NDPPP to use at most 10 threads + combineMS.argument.msin = make_concatmap.output.mapfile # use the mapfile from the make_concatmap step + combineMS.argument.msin.datacolumn = DATA # read from the DATA column + combineMS.argument.steps = [] # don't really do anything with the data + combineMS.argument.msin.missingdata = True # fill missing data with flagged dummy values + + # 9th step + # calibrate the combined MS (we copied the uncorrected data) + # nearly the same as step 3 (and step 5), but this time we work on the combined MS and change "CellSize.Freq" + reCalibrate.control.type = python-calibrate-stand-alone # run BBS on single files + reCalibrate.control.max_per_node = 20 # run at most 20 BBS processes per node + reCalibrate.argument.force = True # force replaceing of parmDB and skyDB + reCalibrate.argument.observation = combineMS.output.mapfile # run on file generated by combineMS step + reCalibrate.argument.parset = {{ bbs_calibration_parset }} # which parset to use (path specified above) + reCalibrate.argument.catalog = {{ calibrator_skymodel }} # which skymodel to use + reCalibrate.argument.Step.solve.Solve.CellSize.Freq = 4 # overwrite one parameter in the BBS parset + + # 10th step + # make a clean image of the combined data with awimager + imageCombined.control.type = awimager # run the awimager + imageCombined.control.max_per_node = 1 # run only one process at a time + imageCombined.control.environment = {OMP_NUM_THREADS: 20} # maximum number of parallel threads + imageCombined.argument.ms = combineMS.output.mapfile # run on files generated by flag_compress step + imageCombined.argument.data = CORRECTED_DATA # read from the CORRECTED_DATA column + imageCombined.argument.weight = briggs # further imaging parameters ... + imageCombined.argument.robust = 0 + imageCombined.argument.npix = 4096 + imageCombined.argument.cellsize = 5.5arcsec + imageCombined.argument.padding = 1.2 + imageCombined.argument.stokes = I + imageCombined.argument.operation = mfclark + imageCombined.argument.wmax = 20000 + imageCombined.argument.UVmin = 0.08 + imageCombined.argument.UVmax = 18 + imageCombined.argument.niter = 5000 + imageCombined.argument.threshold = 0.1Jy + + # 11th step + # subtract 3C295 from the data + subtract3C295.control.type = python-calibrate-stand-alone # run BBS on single files + subtract3C295.control.max_per_node = 20 # run at most 20 BBS processes per node + subtract3C295.argument.force = False # keep old parmDB and skyDB + subtract3C295.argument.observation = combineMS.output.mapfile # run on file generated by combineMS step + subtract3C295.argument.parset = {{ bbs_subtraction_parset }} # which parset to use (path specified above) + subtract3C295.argument.catalog = {{ calibrator_skymodel }} # which skymodel to use + + # 12th step: + # run NDPPP on all files, this will generate new files with the compressed data + copySubtracted.control.type = dppp # run ndppp + copySubtracted.control.max_per_node = 4 # just my standard... + copySubtracted.control.environment = {OMP_NUM_THREADS: 8} # just my standard... + copySubtracted.argument.msin = combineMS.output.mapfile # data that was used in subtract3C295 step + copySubtracted.argument.msin.datacolumn = 3C295_SUBTRACTED # read data from the column it was written to + copySubtracted.argument.steps = [] # don't really do anything with the data + + # 13th step + # generate a mapfile that points to the parmdb generated in step 9 + # i.e. take the file-name(s) used in step 9 and add "/instrument" to them + parmDBmap.control.kind = plugin # plugin, -> short non-parallel step + parmDBmap.control.type = changeMapfile + parmDBmap.control.mapfile_in = combineMS.output.mapfile + parmDBmap.control.join_files = instrument + parmDBmap.control.newname = parmdb.mapfile + + # 14th step + # Apply old calibration to the data + applycal.control.type = python-calibrate-stand-alone # run BBS on single files + applycal.control.max_per_node = 20 # run at most 20 BBS processes per node + applycal.argument.observation = copySubtracted.output.mapfile # run on file generated by combineMS step + applycal.argument.parmdb = parmDBmap.output.mapfile # mapfile with the existing parmDB(s) to use + applycal.argument.parset = {{ bbs_correct_parset }} # which parset to use + applycal.argument.catalog = {{ calibrator_skymodel }} # which skymodel to use + + # 15th step + # make a clean image of the data where we subtracted 3C925 with awimager + imageField.control.type = awimager # run the awimager + imageField.control.max_per_node = 1 # run only one process at a time + imageField.control.environment = {OMP_NUM_THREADS: 20} # maximum number of parallel threads + imageField.argument.ms = copySubtracted.output.mapfile # read input MS here + imageField.argument.data = CORRECTED_DATA # read from the CORRECTED_DATA column + imageField.argument.weight = briggs # further imaging parameters ... + imageField.argument.robust = 0 + imageField.argument.npix = 4096 + imageField.argument.cellsize = 5.5arcsec + imageField.argument.padding = 1.2 + imageField.argument.stokes = I + imageField.argument.operation = mfclark + imageField.argument.wmax = 20000 + imageField.argument.UVmin = 0.08 + imageField.argument.UVmax = 18 + imageField.argument.niter = 1000 + imageField.argument.threshold = 30mJy + +Installation on Jureca +---------------------- +The installation process has changed over the past years and is now relying mostly on the system components. Only install what is not provided by the system. +The current local installation is in the home directory of user ``htb003``. For any mentioned script please look there. +To find out which modules to load use the ``module avail`` command. To get more information about a specific module or to search for specific software use ``module spider``. +The latest install is Lofar version 2.17 and it uses the following modules from the Jureca software stack 2016a: +:: + + module load GCC/5.3.0-2.26 ParaStationMPI/5.1.5-1 + module load Python/2.7.11 + module load CMake/3.4.3 + module load Boost/1.60.0-Python-2.7.11 + module load GSL/2.1 + module load HDF5/1.8.16 + module load flex/2.6.0 + module load XML-LibXML/2.0124-Perl-5.22.1 + module load SciPy-Stack/2016a-Python-2.7.11 + +In addition to these modules the Lofar software is depending on ``cfitsio``, ``wcslib``, ``casacore``, ``casacore-python``(pyrap), ``casarest``, ``aoflagger``. In preperation to compile the rest of the software the following paths are set with a a shell script. +For different versions of the software there are different scripts for loading the appropriate environment. This for example is what the Lofar version 2.17 with Jureca stack2016a looks like: +:: + + #!/bin/sh + export PYTHONPATH=/homea/htb00/htb003/local_jureca_stack2016a/lib/python2.7/site-packages + export PYTHONPATH=/homea/htb00/htb003/lofar_jureca_2.17_stack2016a/lib/python2.7/site-packages:$PYTHONPATH + #export PYTHONHOME=/homea/htb00/htb003/local_jureca_stack2016a + # + export PATH=/homea/htb00/htb003/local_jureca_stack2016a/bin:$PATH + export PATH=/homea/htb00/htb003/lofar_jureca_2.17_stack2016a/bin:$PATH + export PATH=/homea/htb00/htb003/lofar_jureca_2.17_stack2016a/sbin:$PATH + # + export LD_LIBRARY_PATH=/homea/htb00/htb003/lofar_jureca_2.17_stack2016a/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=/homea/htb00/htb003/lofar_jureca_2.17_stack2016a/lib64:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=/homea/htb00/htb003/local_jureca_stack2016a/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=/homea/htb00/htb003/local_jureca_stack2016a/lib64:$LD_LIBRARY_PATH + # + export LOFAR_BUILD_DIR=/homea/htb00/htb003 + export LOFAR_MAKER=release + export F77=gfortran + export FC=gfortran + export BLAS=/usr/local/software/jureca/Stages/2016a/software/OpenBLAS/0.2.15-GCC-5.3.0-2.26-LAPACK-3.6.0/lib/libopenblas.so + export LAPACK=/usr/local/software/jureca/Stages/2016a/software/OpenBLAS/0.2.15-GCC-5.3.0-2.26-LAPACK-3.6.0/lib/libopenblas.so + export LOFARROOT=${LOFAR_BUILD_DIR}/lofar_jureca_2.17_stack2016a + # + module load GCC/5.3.0-2.26 ParaStationMPI/5.1.5-1 + module load Python/2.7.11 + module load CMake/3.4.3 + module load Boost/1.60.0-Python-2.7.11 + module load GSL/2.1 + module load HDF5/1.8.16 + module load flex/2.6.0 + module load XML-LibXML/2.0124-Perl-5.22.1 + module load SciPy-Stack/2016a-Python-2.7.11 + export PYTHONSTARTUP=/homea/htb00/htb003/pythonstart + # + export CC=/usr/local/software/jureca/Stages/2016a/software/GCCcore/5.3.0/bin/gcc + export CXX=/usr/local/software/jureca/Stages/2016a/software/GCCcore/5.3.0/bin/g++ + # + export PKG_CONFIG_PATH=/homea/htb00/htb003/local_jureca_stack2016a/lib/pkgconfig:$PKG_CONFIG_PATH + # since Lofar 2.15. Flags for dependency building of aoflagger + # only for building aoflagger + export LDFLAGS=-L/homea/htb00/htb003/local_jureca_stack2016a/lib + export CPPFLAGS=-L/homea/htb00/htb003/local_jureca_stack2016a/include + # + export GSETTINGS_SCHEMA_DIR=/homea/htb00/htb003/local_jureca/share/glib-2.0/schemas + +In addition to this environment there have always been little changes that had to be made to ensure proper compiling and installation of the rest of the software. These changes differed from version to version so that no general solution can be presented. +The follwong only shows one possible guideline. + +cfitsio, wcslib +^^^^^^^^^^^^^^^ +Nothing special here. Just go the normal configure, make, make install way and add the ``prefix`` option to configure to install the libraries in userspace. + +Casacore, Python-Casacore, Casarest +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In general proceed as instructed on the project webpages for these packages. On Jureca the program ``ccmake`` in contrast to just calling ``cmake`` was used to configure the paths to all required libraries and the proper compiler version. +The cmake configuration files usualy do not find the correct libraries configured by the settings above as they are not in standard directories of fresh system installs. + +AOFlagger +^^^^^^^^^ +There were problems with the cmake project files. Because none of the software packages are in standard places the "FOUND" value for GTKMM and BOOST_ASIO might have to be set to true in the CMakeLists file in order to compile everything. +Not sure if this problem exists in the latest version. But without the graphical programs the compile should be straight forward. + +**RFIGUI, PGPLOT** + +Graphical programs are only available for the older Jureca software stack and can be loaded with ``env_lofar_2.15.sh`` and ``env_lofar_2.17.sh`` (authors comment: i recommend to not try to comile those in future versions if the system is not providing the gtkmm package. +compiling the gtk packages and integrating it in the environment is a major pain.). + +WSClean +^^^^^^^ +Nothing special. Take care of setting all options to the proper values for your locally (or not standard) installed libraries. + +Lofar Software Stack +^^^^^^^^^^^^^^^^^^^^ +To compile the Lofar software with a compiler in a custom location you have to edit the file ``CMake/variants/GNU.cmake``. This is also the place to change additional compiler options. This file gets read every time you use cmake for the Lofar software and overwrites changes you make in ccmake or in the CMakeCache.txt file. + +In the latest releases there was a weired problem with the ``shared`` option for the PyBDSM target. It only compiled when forcing the option via the environment variable ``LDFLAGS="-shared"``. + +Again, double check all paths you set in for the options in cmake. + + +Python Packages +^^^^^^^^^^^^^^^ +The least troublesome way to install additional python packages was to download them individually and use their recommended install process. Which is mostly callsing ``setup.py``. Give the option for the local install path you chose (can differ from package to package). +You can also use ``pip`` to install locally but it does not work in all cases. Sometimes it was not detecting that dependencies were already installed in the system paths and tried to overwrite them with diefferent version. +Installing with ``pip`` without dependencies the option is ``--no-deps``. Examples: +:: + + pip install APLpy --no-deps --target /homea/htb00/htb003/local_jureca_stack2016a/lib/python2.7/site-packages + + or using a setup.py in a local source directory: + + python setup.py install --prefix=/homea/htb00/htb003/local_jureca_stack2016a + +GSM Database +^^^^^^^^^^^^ +Log into head node jrl09 and run the script ``/homea/htb00/htb003/start_gsm_database.sh``. It has to be node 09 because that one is hardcoded in the ``gsm.py``. + +Notes for Developers +-------------------- +Some more deatiled information. + +Structure +^^^^^^^^^ +The structure of the generic pipeline has four levels. The parset, the pipeline parser, the master scripts and the node scripts. So very similar to the old pipeline structure. + +**Parset** + +The parset describes the pipeline as explained in the documentation. + +**Pipeline parser** + +The ``genericpipeline.py`` is the parser for any pipeline parset written in the generic pipeline way. Here the steps are created, keywords replaced and where the internal result dictionary is held. +This is pretty much complete. This is pretty much complete and is only extended when users find something limiting or bugs. Latest adddition for example was to enable the use of environment variables in the replacement mechanism. +Or to give the possibility to split the steplist in sublists so they are easier to read and to group by theme. + +There are some little remnants in the code from an attempt to make the construction of a pipeline interactive in a pythonconsole. Adding methods to add steps in specific places or getting output of all possible steps. Not sure if its worth to pursue this approach. + + +**master scripts** + +The master script for the generic pipeline is the ``executable_args.py``. But also the older framework master scripts can be used (Slightliy different calling convention. Please compare msss pipelines with prefactor.). There is only one master script because basically a step is one program that needs some input arguments and the return code needs to be tracked. This can in principle be done in a single script. Maybe a more basic script would better and then one could derive own versions of that class to handle outputs differently. + +**node scripts** + +There are only a few new node scripts. One general ``executable_args.py`` and for specific purpose ``python_plugin.py``, ``executable_casa.py`` and ``calibrate-stand-alone.py``. The casa and calibrate scripts needed too much special tinkering to put them into the executable_args as well. +All of these scripts can be called from the one new master script. + +Error reporting +^^^^^^^^^^^^^^^ +The most prominant thing missing from the generic pipeline framework are meaningful error messages. Because the structure of the redesign was not clear from the beginning error checking has not been top priority. +For validity checking of arguments one would need a reference. But there are no interface definitions for possible steps in a pipeline. Theese would have to be defined first to have meaningful argument checking. +For the control arguments there should be better checks. For example if there is no mapfile give out the message that there is no mapfile specified instead of the out of bounds on the mapfile array. + +Mapfiles +^^^^^^^^ +Another important thing is handling of mapfiles. The plugins mostly exist because of the lack of possibilities to create mapfiles with the standard framework. Now the utility code for mapfiles is copy pasted across different plugins... that is bad. +So there needs to be an overhaul of the mapfile class of the framework. The additional functionality from the dataproduct and multidataproduct classes should be merged into the framework. This way the plugins would contain less code or could be replaced with proper python steps in the pipelines. +What is mainly missing is the possibility to create mapfiles from given folders with file name patterns. Split and merge mapfiles and create proper file fields in the mapfiles for concat and split operations of ``dppp``. Other programs like ``wsclean`` might need different formatting of files lists (having external programs conform to interfaces would be better though). + +Parset +^^^^^^ +The framework allows to create two different kinds of parset objects. This caused some confusion more than once and the classes need to be unified or be made consistent some other way. +There is a class ``Parset`` in ``CEP/Pipeline/framework/lofarpipe/support/parset.py`` and one in ``CEP/Pipeline/framework/lofarpipe/cuisine/parset.py``. And then there is the ``pyparameterset`` class. Someone needs to take a look at all that. \ No newline at end of file diff --git a/CEP/Pipeline/framework/CMakeLists.txt b/CEP/Pipeline/framework/CMakeLists.txt index b628f2e5821e562302470ac6965bd5c18865d33b..736e7e26114464eb13d31905e69fbd1364229282 100644 --- a/CEP/Pipeline/framework/CMakeLists.txt +++ b/CEP/Pipeline/framework/CMakeLists.txt @@ -1,5 +1,5 @@ # $Id$ -lofar_package(Pipeline-Framework 0.1) +lofar_package(Pipeline-Framework 0.1 DEPENDS MessageBus) add_subdirectory(lofarpipe) diff --git a/CEP/Pipeline/framework/lofarpipe/support/control.py b/CEP/Pipeline/framework/lofarpipe/support/control.py index fb5cf8950066675a38bb3f5b7300abcb76e5eda8..b95ac9f03099d7ca635c0d6ea8aa131dfd4ae85d 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/control.py +++ b/CEP/Pipeline/framework/lofarpipe/support/control.py @@ -93,7 +93,7 @@ class control(StatefulRecipe): indicates failure. """ - if self.feedback_method == "messagebus": + if self.feedback_method == "messagebus" and self.feedback_send_status: bus = messagebus.ToBus("lofar.task.feedback.state") msg = TaskFeedbackState( "lofarpipe.support.control", @@ -134,6 +134,11 @@ class control(StatefulRecipe): except: self.feedback_method = "messagebus" + try: + self.feedback_send_status = self.config.getboolean('feedback', 'send_status') + except: + self.feedback_send_status = True + if self.feedback_method == "messagebus" and not messagebus.MESSAGING_ENABLED: self.logger.error("Feedback over messagebus requested, but messagebus support is not enabled or functional") return 1 diff --git a/CEP/Pipeline/framework/lofarpipe/support/jobserver.py b/CEP/Pipeline/framework/lofarpipe/support/jobserver.py index 1e2f724730f6a22f29d3aa68b62f343198aa8227..a88a795a422920d5e79441266133c5bb672d5794 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/jobserver.py +++ b/CEP/Pipeline/framework/lofarpipe/support/jobserver.py @@ -21,7 +21,7 @@ import cPickle as pickle from lofarpipe.support.lofarexceptions import PipelineQuit from lofarpipe.support.pipelinelogging import log_process_output -from lofarpipe.support.utilities import spawn_process +from lofarpipe.support.utilities import spawn_process, socket_recv class JobStreamHandler(SocketServer.StreamRequestHandler): """ @@ -41,17 +41,20 @@ class JobStreamHandler(SocketServer.StreamRequestHandler): Each request is expected to be a 4-bute length followed by either a GET/PUT request or a pickled LogRecord. """ + while True: - chunk = self.request.recv(4) + # Read message length try: + chunk = socket_recv(self.request, 4) slen = struct.unpack(">L", chunk)[0] except: break - chunk = self.connection.recv(slen) - while len(chunk) < slen: - chunk = chunk + self.connection.recv(slen - len(chunk)) - input_msg = chunk.split(" ", 2) + + # Read message + chunk = socket_recv(self.request, slen) try: + input_msg = chunk.split(" ", 2) + # Can we handle this message type? if input_msg[0] == "GET": self.send_arguments(int(input_msg[1])) @@ -122,11 +125,13 @@ class JobSocketReceiver(SocketServer.ThreadingTCPServer): # until the queue is empty, thereby avoiding the problem of falling # out of the logger threads before all log messages have been handled. def loop_in_thread(): + poller = select.poll() + poller.register(self.socket.fileno(), select.POLLIN) + while True: - rd, wr, ex = select.select( - [self.socket.fileno()], [], [], self.timeout - ) - if rd: + events = poller.poll(self.timeout) + + if events: self.handle_request() elif self.abort: break diff --git a/CEP/Pipeline/framework/lofarpipe/support/lofarnode.py b/CEP/Pipeline/framework/lofarpipe/support/lofarnode.py index ab028f856f45ca341402ac206534c878cb0e9fcd..c8127e4db51629146070fa4e3f8ee89b33648a47 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/lofarnode.py +++ b/CEP/Pipeline/framework/lofarpipe/support/lofarnode.py @@ -16,6 +16,7 @@ import logging.handlers import cPickle as pickle from lofarpipe.support.usagestats import UsageStats +from lofarpipe.support.utilities import socket_recv def run_node(*args): """ @@ -128,18 +129,25 @@ class LOFARnodeTCP(LOFARnode): while True: tries -= 1 try: + # connect s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__try_connect(s) + + # send request message = "GET %d" % self.job_id s.sendall(struct.pack(">L", len(message)) + message) - chunk = s.recv(4) + + # receive response length + chunk = socket_recv(s, 4) slen = struct.unpack(">L", chunk)[0] - chunk = s.recv(slen) - while len(chunk) < slen: - chunk += s.recv(slen - len(chunk)) + + # receive response + chunk = socket_recv(s, slen) + + # parse response self.arguments = pickle.loads(chunk) - except socket.error, e: - print "Failed to get recipe arguments from server" + except (IOError, socket.error) as e: + print "Failed to get recipe arguments from server: %s" % (e,) if tries > 0: timeout = random.uniform(min_timeout, max_timeout) print("Retrying in %f seconds (%d more %s)." % diff --git a/CEP/Pipeline/framework/lofarpipe/support/remotecommand.py b/CEP/Pipeline/framework/lofarpipe/support/remotecommand.py index 22abc4efe30970396a9b6028c0936168339e9507..4df285da05bca4ddc0c2ca5db638c57c36e2bc93 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/remotecommand.py +++ b/CEP/Pipeline/framework/lofarpipe/support/remotecommand.py @@ -17,57 +17,24 @@ import time import xml.dom.minidom as xml from lofarpipe.support.pipelinelogging import log_process_output -from lofarpipe.support.utilities import spawn_process +from lofarpipe.support.subprocessgroup import SubProcessGroup from lofarpipe.support.lofarexceptions import PipelineQuit from lofarpipe.support.jobserver import job_server import lofarpipe.support.lofaringredient as ingredient from lofarpipe.support.xmllogging import add_child +from subprocess import Popen, PIPE # By default, Linux allocates lots more memory than we need(?) for a new stack # frame. When multiplexing lots of threads, that will cause memory issues. threading.stack_size(1048576) -class ParamikoWrapper(object): - """ - Sends an SSH command to a host using paramiko, then emulates a Popen-like - interface so that we can pass it back to pipeline recipes. - """ - def __init__(self, paramiko_client, command): - self.returncode = None - self.client = paramiko_client - self.chan = paramiko_client.get_transport().open_session() - self.chan.get_pty() - self.chan.exec_command(command) - self.stdout = self.chan.makefile('rb', -1) - self.stderr = self.chan.makefile_stderr('rb', -1) - - def communicate(self): - if not self.returncode: - self.returncode = self.chan.recv_exit_status() - stdout = "\n".join(line.strip() for line in self.stdout.readlines()) + "\n" - stderr = "\n".join(line.strip() for line in self.stdout.readlines()) + "\n" - return stdout, stderr - - def poll(self): - if not self.returncode and self.chan.exit_status_ready(): - self.returncode = self.chan.recv_exit_status() - return self.returncode - - def wait(self): - if not self.returncode: - self.returncode = self.chan.recv_exit_status() - return self.returncode - - def kill(self): - self.chan.close() - -def run_remote_command(config, logger, host, command, env, arguments = None): + +def run_remote_command(config, logger, host, command, env, arguments = None, resources = {}): """ Run command on host, passing it arguments from the arguments list and exporting key/value pairs from env(a dictionary). - Returns an object with poll() and communicate() methods, similar to - subprocess.Popen. + Returns an array of command line arguments to start. This is a generic interface to potentially multiple ways of running commands (SSH, mpirun, etc). The appropriate method is chosen from the @@ -80,13 +47,7 @@ def run_remote_command(config, logger, host, command, env, arguments = None): logger.info("********************** Remote method is %s" % method) - if method == "paramiko": - try: - key_filename = config.get('remote', 'key_filename') - except: - key_filename = None - return run_via_paramiko(logger, host, command, env, arguments, key_filename) - elif method == "mpirun": + if method == "mpirun": return run_via_mpirun(logger, host, command, env, arguments) elif method == "local": return run_via_local(logger, command, arguments) @@ -97,24 +58,41 @@ def run_remote_command(config, logger, host, command, env, arguments = None): elif method == "slurm_srun_cep3": return run_via_slurm_srun_cep3(logger, command, arguments, host) elif method == "custom_cmdline": - return run_via_custom_cmdline(logger, host, command, env, arguments, config) + return run_via_custom_cmdline(logger, host, command, env, arguments, config, resources) + # Hertfordshire cluster + elif method == "pbs_ssh": + return run_via_ssh(logger, host, command, env, arguments) + # Jureca HPC + elif method == "slurm_srun": + return run_via_slurm_srun(logger, command, arguments, host) else: return run_via_ssh(logger, host, command, env, arguments) + +def run_via_slurm_srun(logger, command, arguments, host): + for arg in arguments: + command = command + " " + str(arg) + commandarray = ["srun", "-N 1", "--cpu_bind=map_cpu:none", "-w", host, "/bin/sh", "-c", "hostname && " + command] + # we have a bug that crashes jobs when too many get startet at the same time + # temporary NOT 100% reliable workaround + from random import randint + time.sleep(randint(1, 20)/2.) + ########################## + return commandarray + + def run_via_slurm_srun_cep3(logger, command, arguments, host): logger.debug("Dispatching command to %s with srun" % host) for arg in arguments: command = command + " " + str(arg) - commandstring = ["srun","-N 1","-n 1","-w",host, "/bin/sh", "-c", "hostname && " + command] + commandarray = ["srun","-N 1","-n 1","-w",host, "/bin/sh", "-c", "hostname && " + command] #commandstring = ["srun","-N 1","--cpu_bind=map_cpu:none","-w",host, "/bin/sh", "-c", "hostname && " + command] # we have a bug that crashes jobs when too many get startet at the same time # temporary NOT 100% reliable workaround #from random import randint #time.sleep(randint(0,10)) ########################## - process = spawn_process(commandstring, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process + return commandarray def run_via_mpirun(logger, host, command, environment, arguments): """ @@ -125,47 +103,35 @@ def run_via_mpirun(logger, host, command, environment, arguments): """ logger.debug("Dispatching command to %s with mpirun" % host) mpi_cmd = ["/usr/bin/mpirun", "-host", host] - for key in environment.keys(): - mpi_cmd.extend(["-x", key]) + for key,value in environment.iteritems(): + mpi_cmd.extend(["-x", "%s=%s" % (key,value)]) mpi_cmd.append("--") mpi_cmd.extend(command.split()) # command is split into (python, script) mpi_cmd.extend(str(arg) for arg in arguments) - env = os.environ - env.update(environment) - process = spawn_process(mpi_cmd, logger, env = env) - # mpirun should be killed with a SIGTERM to enable it to shut down the - # remote command. - process.kill = lambda : os.kill(process.pid, signal.SIGTERM) - return process + return mpi_cmd # let the mpi demon manage free resources to start jobs def run_via_mpiexec(logger, command, arguments, host): for arg in arguments: command = command + " " + str(arg) - commandstring = ["mpiexec", "-x", "-np=1", "/bin/sh", "-c", "hostname && " + command] - process = spawn_process(commandstring, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process + commandarray = ["mpiexec", "-x", "-np=1", "/bin/sh", "-c", "hostname && " + command] + return commandarray # start mpi run on cep # TODO: rsync fails on missing ssh key?? def run_via_mpiexec_cep(logger, command, arguments, host): for arg in arguments: command = command + " " + str(arg) - commandstring = ["mpiexec", "-x", "PYTHONPATH", "-x", "LD_LIBRARY_PATH", "-x", "PATH", "-H", host, "/bin/sh", "-c", "hostname ; " + command] - process = spawn_process(commandstring, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process + commandarray = ["mpiexec", "-x", "PYTHONPATH", "-x", "LD_LIBRARY_PATH", "-x", "PATH", "-H", host, "/bin/sh", "-c", "hostname ; " + command] + return commandarray def run_via_local(logger, command, arguments): - commandstring = ["/bin/sh", "-c"] + commandarray = ["/bin/sh", "-c"] for arg in arguments: command = command + " " + str(arg) - commandstring.append(command) - process = spawn_process(commandstring, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process + commandarray.append(command) + return commandarray def run_via_ssh(logger, host, command, environment, arguments): """ @@ -181,11 +147,9 @@ def run_via_ssh(logger, host, command, environment, arguments): commandstring.append(command) commandstring.extend(re.escape(str(arg)) for arg in arguments) ssh_cmd.append('"' + " ".join(commandstring) + '"') - process = spawn_process(ssh_cmd, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process + return ssh_cmd -def run_via_custom_cmdline(logger, host, command, environment, arguments, config): +def run_via_custom_cmdline(logger, host, command, environment, arguments, config, resources): """ Dispatch a remote command via a customisable command line @@ -196,57 +160,55 @@ def run_via_custom_cmdline(logger, host, command, environment, arguments, config with the following strings replaced: {host} := host to execute command on - {command} := bash command line to be executed + {env} := "A=B C=D" string, the environment to set + {docker_env} := "-e A=B -e C=D" string, the environment to set + {command} := command to be executed {uid} := uid of the calling user - {image} := docker.image configuration option {slurm_job_id} := the SLURM job id to allocate resources in + {job_name} := name of this executable or script + + {nr_cores} := number of cores to allocate for this job """ - commandArray = ["%s=%s" % (key, value) for key, value in environment.items()] - commandArray.append(command) - commandArray.extend(re.escape(str(arg)) for arg in arguments) + + # construct {env} + envPairs = ["%s=%s" % (key, value) for key, value in environment.items()] + envStr = " ".join(envPairs) + + # construct {docker-env} + dockerEnvPairs = ["-e %s=%s" % (key, value) for key, value in environment.items()] + dockerEnvStr = " ".join(dockerEnvPairs) + + # construct {command} + commandArray = [command] + [str(x) for x in arguments] commandStr = " ".join(commandArray) - try: - image = config.get('docker', 'image') - except: - image = "lofar" + # Determine job name + def jobname(commandstr): + args = [os.path.basename(x) for x in commandstr.split()] + + if len(args) == 1: + return args[0] - # Construct the full command line, except for {command}, as that itself - # can contain spaces which we don't want to split on. + return args[1] if args[0] == "python" else args[0] + + # Construct the full command line full_command_line = config.get('remote', 'cmdline').format( uid = os.geteuid(), slurm_job_id = os.environ.get("SLURM_JOB_ID"), - docker_image = image, host = host, - command = "{command}" - ).split(' ') - # Fill in {command} somewhere - full_command_line = [x.format(command = commandStr) for x in full_command_line] + env = envStr, + docker_env = dockerEnvStr, - logger.debug("Dispatching command to %s with custom_cmdline: %s" % (host, full_command_line)) + command = commandStr, + job_name = jobname(command), + nr_cores = resources.get("cores", 2), + ).split() - process = spawn_process(full_command_line, logger) - process.kill = lambda : os.kill(process.pid, signal.SIGKILL) - return process - -def run_via_paramiko(logger, host, command, environment, arguments, key_filename): - """ - Dispatch a remote command via paramiko. - - We return an instance of ParamikoWrapper. - """ - logger.debug("Dispatching command to %s with paramiko" % host) - import paramiko - client = paramiko.SSHClient() - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect(host, key_filename = key_filename) - commandstring = ["%s=%s" % (key, value) for key, value in environment.items()] - commandstring.append(command) - commandstring.extend(re.escape(str(arg)) for arg in arguments) - return ParamikoWrapper(client, " ".join(commandstring)) + logger.debug("Dispatching command to %s with custom_cmdline: %s" % (host, full_command_line)) + return full_command_line class ProcessLimiter(defaultdict): """ @@ -284,10 +246,11 @@ class ComputeJob(object): :param command: Full path to command to be run on target host :param arguments: List of arguments which will be passed to command """ - def __init__(self, host, command, arguments = []): + def __init__(self, host, command, arguments = [], resources = {}): self.host = host self.command = command self.arguments = arguments + self.resources = resources self.results = {} self.results['returncode'] = 123456 # Default to obscure code to allow # test of failing ssh connections @@ -313,7 +276,7 @@ class ComputeJob(object): self.results['returncode'] = 1 error.set() return 1 - process = run_remote_command( + cmdarray = run_remote_command( config, logger, self.host, @@ -326,20 +289,15 @@ class ComputeJob(object): "LOFARENV" : os.environ.get('LOFARENV',''), "QUEUE_PREFIX" : os.environ.get('QUEUE_PREFIX','') }, - arguments = [id, jobhost, jobport] + arguments = [id, jobhost, jobport], + resources = self.resources ) - # Wait for process to finish. In the meantime, if the killswitch - # is set (by an exception in the main thread), forcibly kill our - # job off. - while process.poll() == None: - if killswitch.isSet(): - process.kill() - else: - time.sleep(1) - sout, serr = process.communicate() - serr = serr.replace("Connection to %s closed.\r\n" % self.host, "") - log_process_output("Remote command", sout, serr, logger) + # Run and wait for process to finish. + pg = SubProcessGroup(logger=logger, killSwitch=killswitch) + pg.run(cmdarray) + job_successful = (pg.wait_for_finish() is None) + except Exception, e: logger.exception("Failed to run remote process %s (%s)" % (self.command, str(e))) self.results['returncode'] = 1 @@ -348,10 +306,10 @@ class ComputeJob(object): finally: limiter[self.host].release() - if process.returncode != 0: + if not job_successful: logger.error( - "Remote process %s %s failed on %s (status: %d)" % \ - (self.command, self.arguments, self.host, process.returncode) + "Remote process %s %s failed on %s" % \ + (self.command, self.arguments, self.host) ) error.set() @@ -359,13 +317,13 @@ class ComputeJob(object): # add the duration of time_info_end = time.time() self.results["job_duration"] = str(time_info_end - time_info_start) - self.results['returncode'] = process.returncode + self.results['returncode'] = 0 if job_successful else 1 logger.debug( "compute.dispatch results job {0}: {1}: {2}, {3}: {4} ".format( self.id, "job_duration", self.results["job_duration"], "returncode", self.results["returncode"] )) - return process.returncode + return self.results["returncode"] def threadwatcher(threadpool, logger, killswitch): @@ -427,6 +385,62 @@ class RemoteCommandRecipeMixIn(object): if max_per_node: self.logger.info("Limiting to %d simultaneous jobs/node" % max_per_node) + # External cluster stuff + try: + method = self.config.get('remote', 'method') + except: + method = None + # JURECA SLURM + if method == 'slurm_srun': + nodeliststr = [] + hargs = ['srun','hostname'] + proc = Popen(hargs, False, stdout=PIPE, stderr=None) + tup = proc.communicate() + nodeliststr = tup[0].rstrip('\n').split('\n') + # remove duplicates. order not important + nodeliststr = list(set(nodeliststr)) + + # equal distribution + total = len(jobs) + # when nodes crash? length of slurm_nodelist and env slurm_nnodes dont match anymore + nnodes = len(nodeliststr) + # round robin + nodelist = [] + for i in range(total): + nodelist.append(nodeliststr[i%nnodes]) + + for i, job in enumerate(jobs): + job.host = nodelist[i] + + # Hertfordshire cluster + if method == 'pbs_ssh': + # special case - get the list of nodes from the pbs job + nodeliststr = [] + + try: + filename = os.environ['PBS_NODEFILE'] + except KeyError: + self.logger.error('PBS_NODEFILE not found.') + raise PipelineQuit() + + with open(filename, 'r') as file: + for line in file: + node_name = line.split()[0] + if node_name not in nodeliststr: + nodeliststr.append(node_name) + + # equal distribution + total = len(jobs) + # when nodes crash? length of slurm_nodelist and env slurm_nnodes dont match anymore + nnodes = len(nodeliststr) + # round robin + nodelist = [] + for i in range(total): + nodelist.append(nodeliststr[i%nnodes]) + + for i, job in enumerate(jobs): + job.host = nodelist[i] + with job_server(self.logger, jobpool, self.error) as (jobhost, jobport): self.logger.debug("Job dispatcher at %s:%d" % (jobhost, jobport)) for job_id, job in enumerate(jobs): diff --git a/CEP/Pipeline/framework/lofarpipe/support/subprocessgroup.py b/CEP/Pipeline/framework/lofarpipe/support/subprocessgroup.py index 5eb731e7c416d983eb32488535b2756e73f7bedd..cf966a224dd7f8861c1bcbc8e295622d6e9720b7 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/subprocessgroup.py +++ b/CEP/Pipeline/framework/lofarpipe/support/subprocessgroup.py @@ -1,7 +1,134 @@ -import subprocess +import select +import os +import sys +import fcntl import time from lofarpipe.support.lofarexceptions import PipelineException +# subprocess is broken in python <=2.6. It does not work for fds > 1024 for example. +try: + import subprocess27 as subprocess + print >> sys.stderr, __file__, ": Using Python 2.7 subprocess module!" +except ImportError: + import subprocess + print >> sys.stderr, __file__, ": Using default subprocess module!" + +class SubProcess(object): + STDOUT = 1 + STDERR = 2 + + def __init__(self, logger, cmd, cwd): + """ + Start a subprocess for `cmd' in working directory `cwd'. + + Output is sent to `logger', or stdout if logger is None. + """ + + def print_logger(line): + print line + + self.cmd = cmd + self.killed = False + self.completed = False + self.logger = logger.info if logger else print_logger + + # report we are starting + self.logger("Subprocess starting: %s (%s)" % (" ".join(self.cmd), self.cmd)) + + # start process + self.process = subprocess.Popen( + cmd, + cwd=cwd, + + # Set buffering parameters + bufsize=1, # 1 = line buffering + universal_newlines=True, # translate ^M output by ssh -tt + + # Don't inherit our fds after fork() + close_fds=True, + + # I/O redirection: block stdin, read stdout/stderr separately + stdin=file("/dev/null"), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.pid = self.process.pid + self.exit_status = None + + # output source streams + self.output_streams = { self.STDOUT: self.process.stdout, + self.STDERR: self.process.stderr } + + # output buffer + self.output_buffers = { self.STDOUT: "", + self.STDERR: "" } + + # output sink + self.output_loggers = { self.STDOUT: logger.debug if logger else print_logger, + self.STDERR: logger.warn if logger else print_logger } + + + # Set fds to non-blocking to allow <4k reads. This is needed if the process + # alternates between stdout and stderr output. + for f in self.output_streams.values(): + flag = fcntl.fcntl(f, fcntl.F_GETFL) + fcntl.fcntl(f, fcntl.F_SETFL, flag | os.O_NONBLOCK) + + def done(self): + if self.completed: + return True + + if self.output_streams: + return False + + # Process is finished, read remaining data and exit code + (stdout, stderr) = self.process.communicate() + self.exit_status = self.process.returncode + + self._addoutput(self.STDOUT, stdout, flush=True) + self._addoutput(self.STDERR, stderr, flush=True) + + self.completed = True + + self.logger("Subprocess completed with exit status %d: %s" % (self.exit_status, " ".join(self.cmd))) + + return True + + def kill(self): + if self.killed: + return + + self.process.kill() # sends SIGKILL + self.killed = True + + def fds(self): + return self.output_streams.values() + + def read(self, fileno): + if fileno == self.process.stdout.fileno(): + self._addoutput(self.STDOUT, self.process.stdout.read(4096)) + if fileno == self.process.stderr.fileno(): + self._addoutput(self.STDERR, self.process.stderr.read(4096)) + + def _addoutput(self, stdtype, output, flush=False): + buf = self.output_buffers[stdtype] + output + lines = buf.split("\n") + remainder = lines.pop() if lines else "" + + for l in lines: + self.output_loggers[stdtype](l) + + if flush: + self.output_loggers[stdtype](remainder) + remainder = "" + + self.output_buffers[stdtype] = remainder + + # 0-byte read means they closed the fd + if not output: + if stdtype in self.output_streams: + # Don't close (subprocess doesn't like that), + # but do registera to prevent further select()s. + del self.output_streams[stdtype] class SubProcessGroup(object): """ @@ -14,7 +141,8 @@ class SubProcessGroup(object): max_concurrent_processes = 8, # poll each 10 seconds: we have a mix of short and long # running processes - polling_interval = 10): + polling_interval = 10, + killSwitch = None): self.process_group = [] self.logger = logger self.usageStats = usageStats @@ -26,34 +154,30 @@ class SubProcessGroup(object): self.processes_waiting_for_execution = [] self.polling_interval = polling_interval + self.killSwitch = killSwitch + def _start_process(self, cmd, cwd): """ Helper function collection all the coded needed to start a process """ + + # Do nothing if we're stopping + if self.killSwitch and self.killSwitch.isSet(): + return + # About to start a process, increase the counter self.running_process_count += 1 # Run subprocess - process = subprocess.Popen( - cmd, - cwd=cwd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + process = SubProcess(self.logger, cmd, cwd) # save the process - self.process_group.append((False, (cmd, process))) + self.process_group.append(process) # add to resource monitor if available if self.usageStats: self.usageStats.addPID(process.pid) - if self.logger == None: - print "Subprocess started: {0}".format(cmd) - else: - self.logger.info("Subprocess started: {0}".format(cmd)) - - def run(self, cmd_in, unsave=False, cwd=None): """ Add the cmd as a subprocess to the current group: The process is @@ -88,65 +212,62 @@ class SubProcessGroup(object): """ collected_exit_status = [] - while (True): - # The exit test - if (self.running_process_count == 0 and - len(self.processes_waiting_for_execution) == 0): - break # the while loop + while self.running_process_count or self.processes_waiting_for_execution: + # collect all unfinished processes + processes = [p for p in self.process_group if not p.completed] + + # check whether we're stopping + if self.killSwitch and self.killSwitch.isSet(): + for process in processes: + process.kill() + + # collect fds we need to poll -- we need select.poll to support fd > 1024 + poller = select.poll() + fd_lookup = {} + for process in processes: + for fd in process.fds(): + poller.register(fd, select.POLLIN | select.POLLPRI) + fd_lookup[fd.fileno()] = process - # start with waiting - time.sleep(self.polling_interval) + # poll for data + events = poller.poll(self.polling_interval) + + # let processed read their data + for (fileno, _) in events: + fd_lookup[fileno].read(fileno) # check all the running processes for completion - for idx, (completed, (cmd, process)) in \ - enumerate(self.process_group): - if completed: + for process in self.process_group: + if process.completed: + # process completed earlier continue - # poll returns null if the process is not completed - if process.poll() == None: + if not process.done(): + # process still running continue # We have a completed process - # communicate with the process - # TODO: This would be the best place to create a - # non mem caching interaction with the processes! - (stdoutdata, stderrdata) = process.communicate() - exit_status = process.returncode + exit_status = process.exit_status # get the exit status - if exit_status != 0: - collected_exit_status.append((cmd, exit_status)) - - # log the std out and err - if self.logger != None: - self.logger.info( - "Subprocesses group, completed command: ") - self.logger.info(cmd) - self.logger.debug(stdoutdata) - self.logger.warn(stderrdata) - else: - print "Subprocesses group, completed command: " - print cmd - print stdoutdata - print stderrdata + if exit_status != 0: + collected_exit_status.append((process.cmd, exit_status)) # Now update the state of the internal state - self.process_group[idx] = (True, (cmd, process)) self.running_process_count -= 1 - - + # if there are less then the allowed processes running and # we have waiting processes start another on - while (self.running_process_count < self.max_concurrent_processes - and - len(self.processes_waiting_for_execution) != 0): + while self.running_process_count < self.max_concurrent_processes and self.processes_waiting_for_execution: # Get the last process - cmd , cwd = self.processes_waiting_for_execution.pop() + cmd, cwd = self.processes_waiting_for_execution.pop() + # start it self._start_process(cmd, cwd) # If none of the processes return with error status if len(collected_exit_status) == 0: collected_exit_status = None + return collected_exit_status + diff --git a/CEP/Pipeline/framework/lofarpipe/support/utilities.py b/CEP/Pipeline/framework/lofarpipe/support/utilities.py index de7ec3e27956cfdca45d3183911eec56d57bbe12..31bf7c21a6b688a64e12ee4290cdfa25a7938751 100644 --- a/CEP/Pipeline/framework/lofarpipe/support/utilities.py +++ b/CEP/Pipeline/framework/lofarpipe/support/utilities.py @@ -217,6 +217,9 @@ def string_to_list(my_string): def spawn_process(cmd, logger, cwd = None, env = None, max_tries = 2, max_timeout = 30): """ + DEPRECATED -- spawn_process leads to custom, and thus bad, output handling. Use + support.subprocessgroup.SubProcessGroup instead. + Tries to spawn a process. If it hits an OSError due to lack of memory or too many open files, it @@ -225,6 +228,12 @@ def spawn_process(cmd, logger, cwd = None, env = None, max_tries = 2, max_timeou If successful, the process object is returned. Otherwise, we eventually propagate the exception. """ + + logger.error("support.utilities.spawn_process is DEPRECATED. Please use support.subprocessgroup.SubProcessGroup") + + # Make sure the working directory exists. + create_directory(cwd); + trycounter = 0 while True: logger.debug( @@ -290,3 +299,29 @@ def catch_segfaults(cmd, cwd, env, logger, max = 1, cleanup = lambda: None, logger.error("Too many segfaults from %s; aborted" % (cmd[0])) raise subprocess.CalledProcessError(process.returncode, cmd[0]) return process + +def socket_recv(socket, numbytes): + """ + Read numbytes from the given socket. + + Raises IOError if connection has closed before all data could be read. + """ + + data = "" + while numbytes > 0: + try: + chunk = socket.recv(numbytes) + except IOError, e: + if e.errno == errno.EINTR: + continue + else: + raise + + if not chunk: + raise IOError("Connection closed. Received '%s', need %d more bytes" % (data,numbytes)) + + data += chunk + numbytes -= len(chunk) + + return data + diff --git a/CEP/Pipeline/recipes/CMakeLists.txt b/CEP/Pipeline/recipes/CMakeLists.txt index 608dee21588636f4e94c01abcd39c75adca3f453..aa2b46cb431ad739bf080c3ff8310a5d78fecc81 100644 --- a/CEP/Pipeline/recipes/CMakeLists.txt +++ b/CEP/Pipeline/recipes/CMakeLists.txt @@ -1,6 +1,6 @@ # $Id$ -lofar_package(Pipeline-Recipes 0.1) +lofar_package(Pipeline-Recipes 0.1 DEPENDS Docker pyparameterset) # The pipeline.cfg needs to know whether QPID is installed include(LofarFindPackage) diff --git a/CEP/Pipeline/recipes/sip/CMakeLists.txt b/CEP/Pipeline/recipes/sip/CMakeLists.txt index 997cadbab45ed2396ff34892b01dc62e7ea7de53..ed77327d735dd5af81e8662b18bb6aa12e2a8626 100644 --- a/CEP/Pipeline/recipes/sip/CMakeLists.txt +++ b/CEP/Pipeline/recipes/sip/CMakeLists.txt @@ -64,23 +64,8 @@ python_install( plugins/PipelineStep_combineParsets.py DESTINATION lofarpipe/recipes) -lofar_add_bin_scripts( - bin/calibration_pipeline.py - bin/msss_calibrator_pipeline.py - bin/msss_imager_pipeline.py - bin/msss_target_pipeline.py - bin/preprocessing_pipeline.py - bin/imaging_pipeline.py - bin/pulsar_pipeline.py - bin/long_baseline_pipeline.py - bin/selfcal_imager_pipeline.py - bin/startPython.sh - bin/startPythonVersion.sh - bin/stopPython.sh - bin/genericpipeline.py - bin/loader.py - external/bad_station_detection/asciistats.py - external/bad_station_detection/statsplot.py) +add_subdirectory(bin) +add_subdirectory(external/bad_station_detection) install(FILES demixing/bbs_CasA.parset @@ -111,7 +96,9 @@ install(FILES install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pipeline.cfg + ${CMAKE_CURRENT_BINARY_DIR}/pipeline.cfg.CEP4 ${CMAKE_CURRENT_BINARY_DIR}/tasks.cfg + ${CMAKE_CURRENT_BINARY_DIR}/tasks.cfg.CEP4 DESTINATION share/pipeline) # PIPELINE_FEEDBACK_METHOD is used in pipeline.cfg.in to enable/disable qpid feedback @@ -125,7 +112,34 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/pipeline.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/pipeline.cfg) +# Convert configuration files through docker-template +foreach(_file pipeline.cfg.CEP4) + # _src -> _dst + set(_src ${CMAKE_CURRENT_SOURCE_DIR}/${_file}.tmpl) + set(_dst ${CMAKE_CURRENT_BINARY_DIR}/${_file}) + + # add generating command, and (any) target to force the generation + # when "all" is build. + add_custom_command( + OUTPUT ${_dst} + COMMAND ${CMAKE_SOURCE_DIR}/Docker/docker-template -v ${CMAKE_BINARY_DIR}/Docker/versiondocker < ${_src} > ${_dst} + DEPENDS ${CMAKE_SOURCE_DIR}/Docker/docker-template ${_src} ${CMAKE_BINARY_DIR}/Docker/versiondocker + ) + add_custom_target(${_file}_target ALL DEPENDS ${_dst}) + + # install resulting file + install(FILES + ${_dst} + DESTINATION share/pipeline + RENAME Dockerfile + ) +endforeach() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/tasks.cfg) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cfg.CEP4.in + ${CMAKE_CURRENT_BINARY_DIR}/tasks.cfg.CEP4) + diff --git a/CEP/Pipeline/recipes/sip/bin/CMakeLists.txt b/CEP/Pipeline/recipes/sip/bin/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..53d89911854d0ef13b1e9f7f7a5f811ba362bfbc --- /dev/null +++ b/CEP/Pipeline/recipes/sip/bin/CMakeLists.txt @@ -0,0 +1,19 @@ +# $Id: CMakeLists.txt 34753 2016-06-20 10:43:42Z schaap $ + +lofar_add_bin_scripts( + calibration_pipeline.py + msss_calibrator_pipeline.py + msss_imager_pipeline.py + msss_target_pipeline.py + preprocessing_pipeline.py + imaging_pipeline.py + pulsar_pipeline.py + long_baseline_pipeline.py + selfcal_imager_pipeline.py + runPipeline.sh + startPython.sh + startPythonVersion.sh + stopPython.sh + genericpipeline.py + loader.py + ) diff --git a/CEP/Pipeline/recipes/sip/bin/calibration_pipeline.py b/CEP/Pipeline/recipes/sip/bin/calibration_pipeline.py index b4b3a869030ccb070e2e704c3d1b0065b23fefc7..18edcaec53a7052200a7a4860b2afa4804c4c54c 100755 --- a/CEP/Pipeline/recipes/sip/bin/calibration_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/calibration_pipeline.py @@ -250,7 +250,8 @@ class calibration_pipeline(control): mapfile_source=bbs_mapfile, mapfile_target=output_correlated_mapfile, mapfiles_dir=mapfile_dir, - mapfile=output_correlated_mapfile + mapfile=output_correlated_mapfile, + allow_move=True ) with duration(self, "copier"): @@ -258,7 +259,8 @@ class calibration_pipeline(control): mapfile_source=parmdb_mapfile, mapfile_target=output_instrument_mapfile, mapfiles_dir=mapfile_dir, - mapfile=output_instrument_mapfile + mapfile=output_instrument_mapfile, + allow_move=True ) # ********************************************************************* diff --git a/CEP/Pipeline/recipes/sip/bin/genericpipeline.py b/CEP/Pipeline/recipes/sip/bin/genericpipeline.py index c094d6d2cd0f465a15a4310c80ef1a016f9cbc64..2528dcadcc4b2e3e13f7e0a56e12084ff93fc9d7 100755 --- a/CEP/Pipeline/recipes/sip/bin/genericpipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/genericpipeline.py @@ -139,11 +139,19 @@ class GenericPipeline(control): # the names will be the prefix for parset subsets pipeline_args = self.parset.makeSubset( self.parset.fullModuleName('pipeline') + '.') - + pipeline_steps = self.parset.makeSubset( + self.parset.fullModuleName('steps') + '.') # ********************************************************************* # forward declaration of things. just for better overview and understanding whats in here. # some of this might be removed in upcoming iterations, or stuff gets added. step_name_list = pipeline_args.getStringVector('steps') + # construct the step name list if there were pipeline.steps.<subset> + for item in pipeline_steps.keys(): + if item in step_name_list: + loc = step_name_list.index(item) + step_name_list[loc:loc] = pipeline_steps.getStringVector(item) + step_name_list.remove(item) + step_control_dict = {} step_parset_files = {} step_parset_obj = {} @@ -180,6 +188,7 @@ class GenericPipeline(control): # plugins are not used at the moment and might better be replaced with master recipes while step_name_list: stepname = step_name_list.pop(0) + self.logger.info("Beginning step %s" % (stepname,)) step = step_control_dict[stepname] #step_parset = step_parset_obj[stepname] inputdict = {} @@ -278,17 +287,20 @@ class GenericPipeline(control): val = subpipeline_parset[k] if not str(k).startswith('!') and not str(k).startswith('pipeline.replace.'): for item in checklist: - if item in str(val): + if item+".output" in str(val): val = str(val).replace(item, stepname + '-' + item) self.parset.add(stepname + '-' + k, str(val)) else: + # remove replacements strings to prevent loading the same key twice + if k in self._keys(self.parset): + self.parset.remove(k) self.parset.add(k, str(val)) for i, item in enumerate(subpipeline_steplist): subpipeline_steplist[i] = stepname + '-' + item for item in step_parset_obj[stepname].keys(): for k in self._keys(self.parset): - if str(k).startswith('!') and item in k or str(k).startswith('pipeline.replace.') and item in k: + if str(k).startswith('!') and item == str(k).strip("! ") or str(k).startswith('pipeline.replace.') and item == str(k)[17:].strip(): self.parset.remove(k) self.parset.add('! ' + item, str(step_parset_obj[stepname][item])) self._replace_values() @@ -299,10 +311,6 @@ class GenericPipeline(control): step_control_dict[name] = step_control_dict[j] step_name_list.insert(0, name) - # remove replacements strings to prevent loading the same key twice - for k in copy.deepcopy(self.parset.keywords()): - if str(k).startswith('!'): - self.parset.remove(k) # loop if kind_of_step == 'loop': @@ -519,8 +527,10 @@ class GenericPipeline(control): replacedict[str(check).lstrip('!').lstrip(' ')] = str(self.parset[check]) if str(check).startswith('pipeline.replace.'): replacedict[str(check).replace('pipeline.replace.', '').lstrip(' ')] = str(self.parset[check]) - #self.logger.info( 'REPLACEDICT: ') - #self.logger.info(replacedict) + #expand environment variables + for k, v in replacedict.items(): + replacedict[k] = os.path.expandvars(v) + for check in self._keys(self.parset): for k, v in reversed(replacedict.items()): if '{{ '+k+' }}' in str(self.parset[check]): diff --git a/CEP/Pipeline/recipes/sip/bin/imaging_pipeline.py b/CEP/Pipeline/recipes/sip/bin/imaging_pipeline.py index acac777e345f8683ab75231a7d50d1cc1abd4a55..93d394a9709ff7458676c4dafe02f69449f6f79f 100755 --- a/CEP/Pipeline/recipes/sip/bin/imaging_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/imaging_pipeline.py @@ -265,8 +265,8 @@ class imaging_pipeline(control): # MS per image. It must be stored on the same host as the final image. self.target_data = copy.deepcopy(self.output_data) - for item in self.target_data: - item.file = os.path.join(self.scratch_directory, 'concat.ms') + for idx, item in enumerate(self.target_data): + item.file = os.path.join(self.scratch_directory, 'ms_per_image_%d' % idx, 'concat.ms') @xml_node diff --git a/CEP/Pipeline/recipes/sip/bin/long_baseline_pipeline.py b/CEP/Pipeline/recipes/sip/bin/long_baseline_pipeline.py index 3375b5ae7a54b758b86ef2469e73c2005cf4e5c2..104aeea30bf3429f750c0385d5451ffdb854a840 100644 --- a/CEP/Pipeline/recipes/sip/bin/long_baseline_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/long_baseline_pipeline.py @@ -206,8 +206,8 @@ class longbaseline_pipeline(control): # MS per image. It must be stored on the same host as the final image. self.target_data = copy.deepcopy(self.output_data) - for item in self.target_data: - item.file = os.path.join(self.scratch_directory, 'concat.ms') + for idx, item in enumerate(self.target_data): + item.file = os.path.join(self.scratch_directory, 'ms_per_image_%d' % idx, 'concat.ms') @xml_node diff --git a/CEP/Pipeline/recipes/sip/bin/msss_calibrator_pipeline.py b/CEP/Pipeline/recipes/sip/bin/msss_calibrator_pipeline.py index 0c627b6cb56006576297eb09b5014c41c162541e..eb641e574a125d85011e5005447ed1b8c0f5d768 100755 --- a/CEP/Pipeline/recipes/sip/bin/msss_calibrator_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/msss_calibrator_pipeline.py @@ -273,7 +273,8 @@ class msss_calibrator_pipeline(control): mapfile_source=bbs_mapfile, mapfile_target=output_correlated_mapfile, mapfiles_dir=mapfile_dir, - mapfile=output_correlated_mapfile + mapfile=output_correlated_mapfile, + allow_move=True ) # ********************************************************************* diff --git a/CEP/Pipeline/recipes/sip/bin/msss_imager_pipeline.py b/CEP/Pipeline/recipes/sip/bin/msss_imager_pipeline.py index 8b075b094d87a9eeeb29b077a73584357959721a..f2ce16de7cad52ae7019ee5ccb71c4459852c269 100755 --- a/CEP/Pipeline/recipes/sip/bin/msss_imager_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/msss_imager_pipeline.py @@ -273,8 +273,8 @@ class msss_imager_pipeline(control): # MS per image. It must be stored on the same host as the final image. self.target_data = copy.deepcopy(self.output_data) - for item in self.target_data: - item.file = os.path.join(self.scratch_directory, 'concat.ms') + for idx, item in enumerate(self.target_data): + item.file = os.path.join(self.scratch_directory, 'ms_per_image_%d' % idx, 'concat.ms') @xml_node diff --git a/CEP/Pipeline/recipes/sip/bin/msss_target_pipeline.py b/CEP/Pipeline/recipes/sip/bin/msss_target_pipeline.py index f628424d523a889d88c2d00d6dca00ae5abab48e..1d8cee64e7ef17be827fa41597a905bc14477f0c 100755 --- a/CEP/Pipeline/recipes/sip/bin/msss_target_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/msss_target_pipeline.py @@ -146,7 +146,8 @@ class msss_target_pipeline(control): mapfile_source=source_path, mapfile_target=target_path, mapfiles_dir=copier_map_path, - mapfile=copied_files_path)['mapfile_target_copied'] + mapfile=copied_files_path, + allow_move=False)['mapfile_target_copied'] # Some copy action might fail; the skip fields in the other map-files # need to be updated these to reflect this. @@ -308,7 +309,8 @@ class msss_target_pipeline(control): mapfile_source=bbs_mapfile, mapfile_target=corrected_mapfile, mapfiles_dir=mapfile_dir, - mapfile=corrected_mapfile + mapfile=corrected_mapfile, + allow_move=True ) # ********************************************************************* diff --git a/CEP/Pipeline/recipes/sip/bin/preprocessing_pipeline.py b/CEP/Pipeline/recipes/sip/bin/preprocessing_pipeline.py index 345fba64f1a573a9d7f75507ef8d41e91fb1aa1d..cc4644ea4a808e6e9e6da02b47217f398d57e7ed 100755 --- a/CEP/Pipeline/recipes/sip/bin/preprocessing_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/preprocessing_pipeline.py @@ -185,7 +185,7 @@ class preprocessing_pipeline(control): # Run the Default Pre-Processing Pipeline (DPPP); with duration(self, "ndppp"): - self.run_task("ndppp", + output_data_mapfile = self.run_task("ndppp", (input_data_mapfile, output_data_mapfile), data_start_time=vdsinfo['start_time'], data_end_time=vdsinfo['end_time'], @@ -196,7 +196,7 @@ class preprocessing_pipeline(control): parset=ndppp_parset, parmdb_mapfile=parmdb_mapfile, sourcedb_mapfile=sourcedb_mapfile - ) + )['mapfile'] # ********************************************************************* # 6. Create feedback file for further processing by the LOFAR framework diff --git a/CEP/Pipeline/recipes/sip/bin/pulsar_pipeline.py b/CEP/Pipeline/recipes/sip/bin/pulsar_pipeline.py index 53d16d41dee4a58f06f341ad260ca95c5f2f63aa..7fb03e3d794d69b3c39462a576731af256a926d8 100755 --- a/CEP/Pipeline/recipes/sip/bin/pulsar_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/pulsar_pipeline.py @@ -18,6 +18,7 @@ import sys import pulp from string import join +import pprint from lofarpipe.support.control import control from lofarpipe.support.data_map import DataMap, validate_data_maps from lofarpipe.support.lofarexceptions import PipelineException @@ -46,6 +47,7 @@ class pulsar_pipeline(control): dps = self.parset.makeSubset( self.parset.fullModuleName('DataProducts') + '.' ) + # Coherent Stokes input data self.coherentStokesEnabled = dps.getBool('Input_CoherentStokes.enabled', False) self.input_data['coherent'] = DataMap([ @@ -113,6 +115,7 @@ class pulsar_pipeline(control): self._get_io_product_specs() self.job_dir = self.config.get("layout", "job_directory") + self.globalfs = self.config.has_option("remote", "globalfs") and self.config.getboolean("remote", "globalfs") parset_dir = os.path.join(self.job_dir, "parsets") mapfile_dir = os.path.join(self.job_dir, "mapfiles") @@ -137,6 +140,12 @@ class pulsar_pipeline(control): self.pulsar_parms = self.parset.makeSubset(self.parset.fullModuleName('Pulsar') + '.') pulsar_parset = os.path.join(parset_dir, "Pulsar.parset") + + if self.globalfs: + # patch for Pulp in case of DOCKER + for k in [x for x in self.pulsar_parms.keys() if x.endswith("_extra_opts")]: + self.pulsar_parms.replace(k, self.pulsar_parms[k].getString().replace(" ","\\\\ ")) + self.pulsar_parms.writeFile(pulsar_parset) self.logger.debug("Processing: %s" % @@ -146,6 +155,12 @@ class pulsar_pipeline(control): # --auto = automatic run from framework # -q = quiet mode, no user interaction sys.argv = ['pulp.py', '--auto', '-q'] + + if self.globalfs: + project = self.parset.getString(self.parset.fullModuleName('Campaign') + '.name') + sys.argv.extend(['--slurm', '--globalfs', '--docker', '--docker-container=lofar-pulp:%s' % os.environ.get("LOFAR_TAG"), '--raw=/data/projects/%s' % project]) + else: + sys.argv.append("--auto") if (not self.coherentStokesEnabled): sys.argv.extend(["--noCS", "--noCV", "--noFE"]) @@ -158,6 +173,7 @@ class pulsar_pipeline(control): # Run the pulsar pipeline self.logger.debug("Starting pulp with: " + join(sys.argv)) + self.logger.debug("Calling pulp.pulp(self) with self = %s", pprint.pformat(vars(self))) p = pulp.pulp(self) # TODO: MUCK self to capture the API # NOTE: PULP returns 0 on SUCCESS!! diff --git a/CEP/Pipeline/recipes/sip/bin/runPipeline.sh b/CEP/Pipeline/recipes/sip/bin/runPipeline.sh new file mode 100755 index 0000000000000000000000000000000000000000..8c40f4a2247de9408baf19e88ed548060444f282 --- /dev/null +++ b/CEP/Pipeline/recipes/sip/bin/runPipeline.sh @@ -0,0 +1,87 @@ +#!/bin/bash -e + +# Entry script from MAC on non-CEP2 clusters (CEP2 uses startPython.sh). +# +# The following chain is executed: +# +# setStatus(STARTED) +# getParset() +# (execute pipeline as specified in parset) +# setStatus(COMPLETING) +# setStatus(FINISHED) +# +# Syntax: +# +# runPipeline.sh -o <obsid> || pipelineAborted.sh -o <obsid> + +# ======= Defaults + +# Obs ID +OBSID= + +# Parset (will be requested if not set) +PARSET= + +# Location of pipeline-framework configuration file +PIPELINE_CONFIG=$LOFARROOT/share/pipeline/pipeline.cfg + +# Directory for parset and feedback files +PARSET_DIR=$LOFARROOT/var/run + +# ======= Parse command-line parameters + +function usage() { + echo "$0 -o OBSID [options]" + echo "" + echo " -o OBSID Task identifier" + echo " -c pipeline.cfg Override pipeline configuration file (default: $PIPELINE_CONFIG)" + echo " -p pipeline.parset Provide parset (default: request through QPID)" + echo " -P dir Directory to which to save parset and feedback files (default: $PARSET_DIR)" + exit 1 +} + +while getopts "ho:c:p:P:" opt; do + case $opt in + h) usage + ;; + o) OBSID="$OPTARG" + ;; + c) PIPELINE_CONFIG="$OPTARG" + ;; + p) PARSET="$OPTARG" + ;; + P) PARSET_DIR="$OPTARG" + ;; + \?) error "Invalid option: -$OPTARG" + ;; + :) error "Option requires an argument: -$OPTARG" + ;; + esac +done +[ -z "$OBSID" ] && usage + +# ======= Init + +if [ -z "$PARSET" ]; then + # Fetch parset + PARSET=${PARSET_DIR}/Observation${OBSID}.parset + getOTDBParset -o $OBSID >$PARSET +fi + +# ======= Run + +# Fetch parameters from parset +PROGRAM_NAME=$(getparsetvalue $PARSET "ObsSW.Observation.ObservationControl.PythonControl.pythonProgram") + +# Run pipeline +OPTIONS=" -d -c $PIPELINE_CONFIG" + +# Set up the environment (information to propagate to the node scripts for monitoring and logging) +export LOFAR_OBSID="$OBSID" + +# Start the Python program +echo "**** $(date) ****" +echo "Executing: ${PROGRAM_NAME} ${OPTIONS} ${PARSET}" + +# Run and propagate SIGTERM, SIGINT +exec ${PROGRAM_NAME} ${OPTIONS} ${PARSET} diff --git a/CEP/Pipeline/recipes/sip/bin/selfcal_imager_pipeline.py b/CEP/Pipeline/recipes/sip/bin/selfcal_imager_pipeline.py index 0d1e725ddf7cf44fffcea1dd13944c229ec07230..040e51e7b9291f16593b20398604de04fc53c519 100644 --- a/CEP/Pipeline/recipes/sip/bin/selfcal_imager_pipeline.py +++ b/CEP/Pipeline/recipes/sip/bin/selfcal_imager_pipeline.py @@ -446,8 +446,8 @@ class selfcal_imager_pipeline(control): # MS per image. It must be stored on the same host as the final image. self.target_data = copy.deepcopy(self.output_data) - for item in self.target_data: - item.file = os.path.join(self.scratch_directory, 'concat.ms') + for idx, item in enumerate(self.target_data): + item.file = os.path.join(self.scratch_directory, 'ms_per_image_%d' % idx, 'concat.ms') @xml_node diff --git a/CEP/Pipeline/recipes/sip/external/bad_station_detection/CMakeLists.txt b/CEP/Pipeline/recipes/sip/external/bad_station_detection/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bca19e5255fd91252d537f1b3d9c5997a69adb74 --- /dev/null +++ b/CEP/Pipeline/recipes/sip/external/bad_station_detection/CMakeLists.txt @@ -0,0 +1,6 @@ +# $Id: CMakeLists.txt 34753 2016-06-20 10:43:42Z schaap $ + +lofar_add_bin_scripts( + asciistats.py + statsplot.py + ) diff --git a/CEP/Pipeline/recipes/sip/master/bbs_reducer.py b/CEP/Pipeline/recipes/sip/master/bbs_reducer.py index 5fda848e56d55e3bc278e8898c1125beef19e4c3..25bab0248d2594ef79d032b65076c6daa5f51621 100644 --- a/CEP/Pipeline/recipes/sip/master/bbs_reducer.py +++ b/CEP/Pipeline/recipes/sip/master/bbs_reducer.py @@ -26,6 +26,11 @@ class bbs_reducer(BaseRecipe, RemoteCommandRecipeMixIn): '-p', '--parset', help="BBS configuration parset" ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'executable': ingredient.ExecField( '--executable', help="The full path to the BBS-reducer executable" @@ -115,7 +120,10 @@ class bbs_reducer(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['executable'], self.inputs['parset'], self.environment - ] + ], + resources={ + "cores": self.inputs['nthreads'] + } ) ) self._schedule_jobs(self.jobs) diff --git a/CEP/Pipeline/recipes/sip/master/copier.py b/CEP/Pipeline/recipes/sip/master/copier.py index 4846c7a5beb6e36de3713196c3f137a472701212..c9295910b2f913f5423a8be496fd8267f7bac4b2 100644 --- a/CEP/Pipeline/recipes/sip/master/copier.py +++ b/CEP/Pipeline/recipes/sip/master/copier.py @@ -142,6 +142,11 @@ class copier(MasterNodeInterface): default=True, help="Allow renaming of basename at target location" ), + 'allow_move': ingredient.BoolField( + '--allow-move', + default=True, + help="Allow moving files instead of copying them" + ), 'mapfiles_dir': ingredient.StringField( '--mapfiles-dir', help="Path of directory, shared by all nodes, which will be used" @@ -236,6 +241,8 @@ class copier(MasterNodeInterface): self.logger.info("Starting copier run") super(copier, self).go() + globalfs = self.config.has_option("remote", "globalfs") and self.config.getboolean("remote", "globalfs") + # Load data from mapfiles self.source_map = DataMap.load(self.inputs['mapfile_source']) self.target_map = DataMap.load(self.inputs['mapfile_target']) @@ -246,7 +253,7 @@ class copier(MasterNodeInterface): # Run the compute nodes with the node specific mapfiles for source, target in zip(self.source_map, self.target_map): - args = [source.host, source.file, target.file] + args = [source.host, source.file, target.file, globalfs, self.inputs['allow_move']] self.append_job(target.host, args) # start the jobs, return the exit status. diff --git a/CEP/Pipeline/recipes/sip/master/dppp.py b/CEP/Pipeline/recipes/sip/master/dppp.py index f8a4467eff36a6e8bc880671c658be2748904662..8cd5cc2d36911fd1b0283fadc8115fd878aec5b0 100644 --- a/CEP/Pipeline/recipes/sip/master/dppp.py +++ b/CEP/Pipeline/recipes/sip/master/dppp.py @@ -229,6 +229,7 @@ class dppp(BaseRecipe, RemoteCommandRecipeMixIn): sdb.file, self.inputs['parset'], self.inputs['executable'], + os.path.join(self.inputs['working_directory'], self.inputs['job_name']), self.environment, self.inputs['demix_always'], self.inputs['demix_if_needed'], @@ -236,7 +237,10 @@ class dppp(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['data_end_time'], self.inputs['nthreads'], self.inputs['clobber'] - ] + ], + resources={ + "cores": self.inputs['nthreads'] + } ) ) self._schedule_jobs(jobs, max_per_node=self.inputs['nproc']) diff --git a/CEP/Pipeline/recipes/sip/master/executable_args.py b/CEP/Pipeline/recipes/sip/master/executable_args.py index 4c07727745f06af08cdf6b0b8a608335383d6800..5fd1472b9f745814c5a190ad6c62ee7b4c9462fc 100644 --- a/CEP/Pipeline/recipes/sip/master/executable_args.py +++ b/CEP/Pipeline/recipes/sip/master/executable_args.py @@ -35,6 +35,11 @@ class executable_args(BaseRecipe, RemoteCommandRecipeMixIn): default='', optional=True ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'nodescript': ingredient.StringField( '--nodescript', help="Name of the node script to execute", @@ -190,6 +195,9 @@ class executable_args(BaseRecipe, RemoteCommandRecipeMixIn): if 'executable' in self.inputs: executable = self.inputs['executable'] + if self.inputs['nthreads']: + self.environment["OMP_NUM_THREADS"] = str(self.inputs['nthreads']) + if 'environment' in self.inputs: self.environment.update(self.inputs['environment']) @@ -369,7 +377,10 @@ class executable_args(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['parsetasfile'], args_format, self.environment - ] + ], + resources={ + "cores": self.inputs['nthreads'] + } ) ) max_per_node = self.inputs['max_per_node'] @@ -380,7 +391,7 @@ class executable_args(BaseRecipe, RemoteCommandRecipeMixIn): if job.results['returncode'] != 0: outp.skip = True if not self.inputs['error_tolerance']: - self.logger.error("A job has failed and error_tolerance is not set. Bailing out!") + self.logger.error("A job has failed with returncode %d and error_tolerance is not set. Bailing out!" % job.results['returncode']) return 1 for k, v in job.results.items(): if not k in jobresultdict: diff --git a/CEP/Pipeline/recipes/sip/master/imager_awimager.py b/CEP/Pipeline/recipes/sip/master/imager_awimager.py index 1e88c5ecb27e79bbebfe9ce598fdac4caf0c3eaf..b1b2365feab84ca29d7fe8b7628cab43ba5594af 100644 --- a/CEP/Pipeline/recipes/sip/master/imager_awimager.py +++ b/CEP/Pipeline/recipes/sip/master/imager_awimager.py @@ -5,6 +5,7 @@ # swinbank@transientskp.org # ------------------------------------------------------------------------------ import sys +import os import copy import lofarpipe.support.lofaringredient as ingredient from lofarpipe.support.baserecipe import BaseRecipe @@ -38,6 +39,11 @@ class imager_awimager(BaseRecipe, RemoteCommandRecipeMixIn): '-p', '--parset', help = "The full path to a awimager configuration parset." ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'working_directory': ingredient.StringField( '-w', '--working-directory', help = "Working directory used on output nodes. Results location" @@ -120,7 +126,7 @@ class imager_awimager(BaseRecipe, RemoteCommandRecipeMixIn): sourcedb_map.iterator = input_map.iterator = output_map.iterator = \ DataMap.SkipIterator - for measurement_item, source_item in zip(input_map, sourcedb_map): + for idx, (measurement_item, source_item) in enumerate(zip(input_map, sourcedb_map)): if measurement_item.skip or source_item.skip: jobs.append(None) continue @@ -129,12 +135,16 @@ class imager_awimager(BaseRecipe, RemoteCommandRecipeMixIn): host , measurement_path = measurement_item.host, measurement_item.file host2 , sourcedb_path = source_item.host, source_item.file + # use unique working directories per job, to prevent interference between jobs on a global fs + working_dir = os.path.join(self.inputs['working_directory'], "imager_awimager_{0}".format(idx)) + # construct and save the output name arguments = [self.inputs['executable'], self.environment, self.inputs['parset'], - self.inputs['working_directory'], - self.inputs['output_image'], + working_dir, + # put in unique dir, as node script wants to put private .par files next to it + "%s_%s/image" % (self.inputs['output_image'], idx), measurement_path, sourcedb_path, self.inputs['mask_patch_size'], @@ -143,7 +153,10 @@ class imager_awimager(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['fov'], ] - jobs.append(ComputeJob(host, node_command, arguments)) + jobs.append(ComputeJob(host, node_command, arguments, + resources={ + "cores": self.inputs['nthreads'] + })) self._schedule_jobs(jobs) # ********************************************************************* diff --git a/CEP/Pipeline/recipes/sip/master/imager_bbs.py b/CEP/Pipeline/recipes/sip/master/imager_bbs.py index c17450c4bff8b07dfc038c4e4de812d06f90a7e8..90d400c51e847a4c7d713880e5891babe96569aa 100644 --- a/CEP/Pipeline/recipes/sip/master/imager_bbs.py +++ b/CEP/Pipeline/recipes/sip/master/imager_bbs.py @@ -36,6 +36,11 @@ class imager_bbs(BaseRecipe, RemoteCommandRecipeMixIn): '-p', '--parset', help="BBS configuration parset" ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'bbs_executable': ingredient.StringField( '--bbs-executable', help="BBS standalone executable (bbs-reducer)" @@ -106,29 +111,32 @@ class imager_bbs(BaseRecipe, RemoteCommandRecipeMixIn): ms_map.iterator = parmdb_map.iterator = sourcedb_map.iterator = \ DataMap.SkipIterator - for (ms, parmdb, sourcedb) in zip(ms_map, parmdb_map, sourcedb_map): + for (idx, (ms, parmdb, sourcedb)) in enumerate(zip(ms_map, parmdb_map, sourcedb_map)): #host is same for each entry (validate_data_maps) host, ms_list = ms.host, ms.file # Write data maps to MultaDataMaps ms_list_path = os.path.join( - map_dir, host + "_ms_" + run_id + ".map") + map_dir, "%s-%s_map_%s.map" % (host, idx, run_id)) MultiDataMap([tuple([host, ms_list, False])]).save(ms_list_path) parmdb_list_path = os.path.join( - map_dir, host + "_parmdb_" + run_id + ".map") + map_dir, "%s-%s_parmdb_%s.map" % (host, idx, run_id)) MultiDataMap( [tuple([host, parmdb.file, False])]).save(parmdb_list_path) sourcedb_list_path = os.path.join( - map_dir, host + "_sky_" + run_id + ".map") + map_dir, "%s-%s_sky_%s.map" % (host, idx, run_id)) MultiDataMap( [tuple([host, [sourcedb.file], False])]).save(sourcedb_list_path) arguments = [self.inputs['bbs_executable'], self.inputs['parset'], ms_list_path, parmdb_list_path, sourcedb_list_path] - jobs.append(ComputeJob(host, node_command, arguments)) + jobs.append(ComputeJob(host, node_command, arguments, + resources={ + "cores": self.inputs['nthreads'] + })) # start and wait till all are finished self._schedule_jobs(jobs) diff --git a/CEP/Pipeline/recipes/sip/master/imager_create_dbs.py b/CEP/Pipeline/recipes/sip/master/imager_create_dbs.py index 10783788c559c2a139ce6c30942b502a5f964274..0c26faaf965dca04b13024e5ed1242c582b8fe49 100644 --- a/CEP/Pipeline/recipes/sip/master/imager_create_dbs.py +++ b/CEP/Pipeline/recipes/sip/master/imager_create_dbs.py @@ -190,8 +190,8 @@ class imager_create_dbs(BaseRecipe, RemoteCommandRecipeMixIn): source_list_map.iterator = slice_paths_map.iterator = \ input_map.iterator = DataMap.SkipIterator - for (input_item, slice_item, source_list_item) in zip( - input_map, slice_paths_map,source_list_map): + for idx, (input_item, slice_item, source_list_item) in enumerate(zip( + input_map, slice_paths_map,source_list_map)): host_ms, concat_ms = input_item.host, input_item.file host_slice, slice_paths = slice_item.host, slice_item.file @@ -199,6 +199,9 @@ class imager_create_dbs(BaseRecipe, RemoteCommandRecipeMixIn): sourcedb_target_path = os.path.join( concat_ms + self.inputs["sourcedb_suffix"]) + # use unique working directories per job, to prevent interference between jobs on a global fs + working_dir = os.path.join(self.inputs['working_directory'], "imager_create_dbs_{0}".format(idx)) + # The actual call for the node script arguments = [concat_ms, sourcedb_target_path, @@ -212,7 +215,7 @@ class imager_create_dbs(BaseRecipe, RemoteCommandRecipeMixIn): slice_paths, self.inputs["parmdb_suffix"], self.environment, - self.inputs["working_directory"], + working_dir, self.inputs["makesourcedb_path"], source_list_item.file, self.inputs["major_cycle"]] diff --git a/CEP/Pipeline/recipes/sip/master/imager_prepare.py b/CEP/Pipeline/recipes/sip/master/imager_prepare.py index 3d06ab9dcd50b41a67c98e774b60389364fb2104..8152f610693eb81c3e6902ef215e898aabd6871f 100644 --- a/CEP/Pipeline/recipes/sip/master/imager_prepare.py +++ b/CEP/Pipeline/recipes/sip/master/imager_prepare.py @@ -57,6 +57,11 @@ class imager_prepare(BaseRecipe, RemoteCommandRecipeMixIn): '-w', '--working-directory', help="Working directory used by the nodes: local data" ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'target_mapfile': ingredient.StringField( '--target-mapfile', help="Contains the node and path to target files, defines" @@ -160,6 +165,8 @@ class imager_prepare(BaseRecipe, RemoteCommandRecipeMixIn): paths_to_image_mapfiles = [] n_subband_groups = len(output_map) # needed for subsets in sb list + globalfs = self.config.has_option("remote", "globalfs") and self.config.getboolean("remote", "globalfs") + for idx_sb_group, item in enumerate(output_map): #create the input files for this node self.logger.debug("Creating input data subset for processing" @@ -172,7 +179,7 @@ class imager_prepare(BaseRecipe, RemoteCommandRecipeMixIn): # Save the mapfile inputs_for_image_mapfile_path = os.path.join( job_directory, "mapfiles", - "ms_per_image_{0}".format(idx_sb_group)) + "ms_per_image_{0}.map".format(idx_sb_group)) self._store_data_map(inputs_for_image_mapfile_path, inputs_for_image_map, "inputmap for location") @@ -189,9 +196,12 @@ class imager_prepare(BaseRecipe, RemoteCommandRecipeMixIn): paths_to_image_mapfiles.append( tuple([item.host, inputs_for_image_mapfile_path, False])) + # use unique working directories per job, to prevent interference between jobs on a global fs + working_dir = os.path.join(self.inputs['working_directory'], "imager_prepare_{0}".format(idx_sb_group)) + arguments = [self.environment, self.inputs['parset'], - self.inputs['working_directory'], + working_dir, self.inputs['processed_ms_dir'], self.inputs['ndppp_exec'], item.file, @@ -203,9 +213,13 @@ class imager_prepare(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['msselect_executable'], self.inputs['rficonsole_executable'], self.inputs['do_rficonsole'], - self.inputs['add_beam_tables']] + self.inputs['add_beam_tables'], + globalfs] - jobs.append(ComputeJob(item.host, node_command, arguments)) + jobs.append(ComputeJob(item.host, node_command, arguments, + resources={ + "cores": self.inputs['nthreads'] + })) # Hand over the job(s) to the pipeline scheduler self._schedule_jobs(jobs) diff --git a/CEP/Pipeline/recipes/sip/master/imager_source_finding.py b/CEP/Pipeline/recipes/sip/master/imager_source_finding.py index 59fd13cfed451994ecb2e58fd87cd25878956c97..bba8b49a5d02362acc421923c3d92d853961c5d2 100644 --- a/CEP/Pipeline/recipes/sip/master/imager_source_finding.py +++ b/CEP/Pipeline/recipes/sip/master/imager_source_finding.py @@ -91,17 +91,20 @@ class imager_source_finding(BaseRecipe, RemoteCommandRecipeMixIn): node_command = " python %s" % (self.__file__.replace("master", "nodes")) jobs = [] input_map.iterator = DataMap.SkipIterator - for item in input_map: + for idx, item in enumerate(input_map): + # use unique working directories per job, to prevent interference between jobs on a global fs + working_dir = os.path.join(self.inputs['working_directory'], "imager_source_finding_{0}".format(idx)) + arguments = [item.file, self.inputs["bdsm_parset_file_run1"], self.inputs["bdsm_parset_file_run2x"], - catalog_output_path, + "%s-%s" % (catalog_output_path, idx), os.path.join( self.inputs["working_directory"], - "bdsm_output.img"), - self.inputs['sourcedb_target_path'], + "bdsm_output-%s.img" % (idx, )), + "%s-%s" % (self.inputs['sourcedb_target_path'], idx), self.environment, - self.inputs['working_directory'], + working_dir, self.inputs['makesourcedb_path'] ] @@ -121,6 +124,9 @@ class imager_source_finding(BaseRecipe, RemoteCommandRecipeMixIn): source_dbs_from_nodes.iterator = \ catalog_output_path_from_nodes.iterator = DataMap.SkipIterator + # Job is succesfull if at least one source_db is found + succesfull_job = False + for job, sourcedb_item, catalog_item in zip(jobs, source_dbs_from_nodes, catalog_output_path_from_nodes): diff --git a/CEP/Pipeline/recipes/sip/master/long_baseline.py b/CEP/Pipeline/recipes/sip/master/long_baseline.py index f61764bc78ba0740108235c66aa2919aab20a808..d5f734b2e34239d20a6d4d7a8998fff59298e8fd 100644 --- a/CEP/Pipeline/recipes/sip/master/long_baseline.py +++ b/CEP/Pipeline/recipes/sip/master/long_baseline.py @@ -63,6 +63,11 @@ class long_baseline(BaseRecipe, RemoteCommandRecipeMixIn): '-w', '--working-directory', help="Working directory used by the nodes: local data" ), + 'nthreads': ingredient.IntField( + '--nthreads', + default=8, + help="Number of threads per process" + ), 'target_mapfile': ingredient.StringField( '--target-mapfile', help="Contains the node and path to target files, defines" @@ -166,6 +171,8 @@ class long_baseline(BaseRecipe, RemoteCommandRecipeMixIn): paths_to_image_mapfiles = [] n_subband_groups = len(output_map) + globalfs = self.config.has_option("remote", "globalfs") and self.config.getboolean("remote", "globalfs") + output_map.iterator = final_output_map.iterator = DataMap.SkipIterator for idx_sb_group, (output_item, final_item) in enumerate(zip(output_map, final_output_map)): @@ -190,9 +197,12 @@ class long_baseline(BaseRecipe, RemoteCommandRecipeMixIn): paths_to_image_mapfiles.append( tuple([output_item.host, inputs_for_image_mapfile_path, False])) + # use a unique working directory per job, to prevent interference between jobs on a global fs + working_dir = os.path.join(self.inputs['working_directory'], "long_baseline_{0}".format(idx_sb_group)) + arguments = [self.environment, self.inputs['parset'], - self.inputs['working_directory'], + working_dir, self.inputs['processed_ms_dir'], self.inputs['ndppp_exec'], output_item.file, @@ -204,9 +214,13 @@ class long_baseline(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['msselect_executable'], self.inputs['rficonsole_executable'], self.inputs['add_beam_tables'], + globalfs, final_item.file] - jobs.append(ComputeJob(output_item.host, node_command, arguments)) + jobs.append(ComputeJob(output_item.host, node_command, arguments, + resources={ + "cores": self.inputs['nthreads'] + })) # Hand over the job(s) to the pipeline scheduler self._schedule_jobs(jobs, max_per_node=self.inputs['nproc']) diff --git a/CEP/Pipeline/recipes/sip/master/rficonsole.py b/CEP/Pipeline/recipes/sip/master/rficonsole.py index a87a9d4dd17bb259eef82cbb60f0563045298aa2..047d78ab2059c841611105ee522fcd5f10b42cfd 100644 --- a/CEP/Pipeline/recipes/sip/master/rficonsole.py +++ b/CEP/Pipeline/recipes/sip/master/rficonsole.py @@ -112,7 +112,10 @@ class rficonsole(BaseRecipe, RemoteCommandRecipeMixIn): self.inputs['indirect_read'], self.inputs['skip_flagged'], self.inputs['working_dir'] - ] + file_list + ] + file_list, + resources={ + "cores": self.inputs['nthreads'] + } ) ) self._schedule_jobs(jobs, max_per_node=self.inputs['nproc']) diff --git a/CEP/Pipeline/recipes/sip/nodes/copier.py b/CEP/Pipeline/recipes/sip/nodes/copier.py index a35f66e5d1c2bb038a7a933be57bc2f7a40f8683..8a30b7a517800e5f5bd39d489078e544f33a2411 100644 --- a/CEP/Pipeline/recipes/sip/nodes/copier.py +++ b/CEP/Pipeline/recipes/sip/nodes/copier.py @@ -21,14 +21,15 @@ class copier(LOFARnodeTCP): """ Node script for copying files between nodes. See master script for full public interface """ - def run(self, source_node, source_path, target_path): + def run(self, source_node, source_path, target_path, globalfs, allow_move): + self.globalfs = globalfs # Time execution of this job with log_time(self.logger): return self._copy_single_file_using_rsync( - source_node, source_path, target_path) + source_node, source_path, target_path, allow_move) def _copy_single_file_using_rsync(self, source_node, source_path, - target_path): + target_path, allow_move): # assure that target dir exists (rsync creates it but.. # an error in the python code will throw a nicer error message = "No write acces to target path: {0}".format( @@ -50,9 +51,12 @@ class copier(LOFARnodeTCP): # construct copy command: Copy to the dir - # if process runs on local host use a simple copy command. - if source_node=="localhost": - command = ["cp", "-r","{0}".format(source_path),"{0}".format(target_path)] + # if process runs on local host use a simple copy or move command. + if self.globalfs or source_node == "localhost": + if allow_move: + command = ["mv", "{0}".format(source_path), "{0}".format(target_path)] + else: + command = ["cp", "-r", "{0}".format(source_path), "{0}".format(target_path)] else: command = ["rsync", "-r", "{0}:{1}/".format(source_node, source_path), diff --git a/CEP/Pipeline/recipes/sip/nodes/dppp.py b/CEP/Pipeline/recipes/sip/nodes/dppp.py index f77761729c52edc1e978753bec84419e063d34b1..6ef03658284f956278bf2bc0850586954c546c2e 100644 --- a/CEP/Pipeline/recipes/sip/nodes/dppp.py +++ b/CEP/Pipeline/recipes/sip/nodes/dppp.py @@ -35,7 +35,7 @@ class dppp(LOFARnodeTCP): """ def run(self, infile, outfile, parmdb, sourcedb, - parsetfile, executable, environment, demix_always, demix_if_needed, + parsetfile, executable, working_directory, environment, demix_always, demix_if_needed, start_time, end_time, nthreads, clobber): """ This function contains all the needed functionality @@ -47,6 +47,7 @@ class dppp(LOFARnodeTCP): self.logger.debug("sourcedb = %s" % sourcedb) self.logger.debug("parsetfile = %s" % parsetfile) self.logger.debug("executable = %s" % executable) + self.logger.debug("working_dir = %s" % working_directory) self.logger.debug("environment = %s" % environment) self.logger.debug("demix_always = %s" % demix_always) self.logger.debug("demix_if_needed = %s" % demix_if_needed) @@ -135,14 +136,14 @@ class dppp(LOFARnodeTCP): # ***************************************************************** # 4. Add ms names to the parset, start/end times if availabe, etc. # 5. Add demixing parameters to the parset - parsetfile, self._prepare_steps(**kwargs) #, unlink=False + parsetfile, self._prepare_steps(**kwargs), output_dir=working_directory, unlink=False ) as temp_parset_filename: self.logger.debug("Created temporary parset file: %s" % temp_parset_filename ) try: - working_dir = tempfile.mkdtemp(suffix=".%s" % (os.path.basename(__file__),)) + working_dir = tempfile.mkdtemp(dir=working_directory,suffix=".%s" % (os.path.basename(__file__),)) # **************************************************************** # 6. Run ndppp cmd = [executable, temp_parset_filename, '1'] diff --git a/CEP/Pipeline/recipes/sip/nodes/imager_awimager.py b/CEP/Pipeline/recipes/sip/nodes/imager_awimager.py index e11471d3d8956c643ce3fcdfe8688f31a8472f6a..8c894ef291d7de05c8d3d5eed6ecba1039405949 100644 --- a/CEP/Pipeline/recipes/sip/nodes/imager_awimager.py +++ b/CEP/Pipeline/recipes/sip/nodes/imager_awimager.py @@ -25,6 +25,7 @@ from lofarpipe.support.pipelinelogging import log_time from lofarpipe.support.utilities import patch_parset from lofarpipe.support.utilities import get_parset from lofarpipe.support.utilities import catch_segfaults +from lofarpipe.support.utilities import create_directory from lofarpipe.support.lofarexceptions import PipelineException import pyrap.tables as pt # @UnresolvedImport from subprocess import CalledProcessError @@ -66,6 +67,10 @@ class imager_awimager(LOFARnodeTCP): # Read the parameters as specified in the parset parset_object = get_parset(parset) + #****************************************************************** + # 0. Create the directories used in this recipe + create_directory(working_directory) + # ************************************************************* # 1. Calculate awimager parameters that depend on measurement set # and the parset diff --git a/CEP/Pipeline/recipes/sip/nodes/imager_create_dbs.py b/CEP/Pipeline/recipes/sip/nodes/imager_create_dbs.py index 0fe1189ac51332120f88f65854d1e77c9d9c4234..30e60ab2a09214e8849173dc34d5064b526eb754 100644 --- a/CEP/Pipeline/recipes/sip/nodes/imager_create_dbs.py +++ b/CEP/Pipeline/recipes/sip/nodes/imager_create_dbs.py @@ -16,6 +16,7 @@ from lofarpipe.support.lofarnode import LOFARnodeTCP from lofarpipe.support.pipelinelogging import log_process_output from lofarpipe.support.pipelinelogging import CatchLog4CPlus from lofarpipe.support.utilities import catch_segfaults +from lofarpipe.support.utilities import create_directory import monetdb.sql as db import lofar.gsm.gsmutils as gsm @@ -65,6 +66,10 @@ class imager_create_dbs(LOFARnodeTCP): self.logger.info("Starting imager_create_dbs Node") self.environment.update(environment) + #****************************************************************** + # 0. Create the directories used in this recipe + create_directory(working_directory) + #******************************************************************* # 1. get a sourcelist: from gsm or from file source_list, append = self._create_source_list( diff --git a/CEP/Pipeline/recipes/sip/nodes/imager_prepare.py b/CEP/Pipeline/recipes/sip/nodes/imager_prepare.py index 63328ef3f2a3e43e84483635109ce1d645d6f66d..b590cc806804ce0c8ed8fcfcb2faf426f413ccad 100644 --- a/CEP/Pipeline/recipes/sip/nodes/imager_prepare.py +++ b/CEP/Pipeline/recipes/sip/nodes/imager_prepare.py @@ -44,17 +44,19 @@ class imager_prepare(LOFARnodeTCP): ndppp_executable, output_measurement_set, time_slices_per_image, subbands_per_group, input_ms_mapfile, asciistat_executable, statplot_executable, msselect_executable, - rficonsole_executable, do_rficonsole, add_beam_tables): + rficonsole_executable, do_rficonsole, add_beam_tables, globalfs): """ Entry point for the node recipe """ self.environment.update(environment) + self.globalfs = globalfs with log_time(self.logger): input_map = DataMap.load(input_ms_mapfile) #****************************************************************** # 1. Create the directories used in this recipe create_directory(processed_ms_dir) + create_directory(os.path.dirname(output_measurement_set)) # time slice dir_to_remove: assure empty directory: Stale data # is problematic for dppp @@ -178,9 +180,10 @@ class imager_prepare(LOFARnodeTCP): command = ["rsync", "-r", "{0}:{1}".format( input_item.host, input_item.file), "{0}".format(processed_ms_dir)] - if input_item.host == "localhost": - command = ["cp", "-r", "{0}".format(input_item.file), - "{0}".format(processed_ms_dir)] + if self.globalfs or input_item.host == "localhost": + # symlinking is enough + command = ["ln", "-sf", "{0}".format(input_item.file), + "-t", "{0}".format(processed_ms_dir)] self.logger.debug("executing: " + " ".join(command)) diff --git a/CEP/Pipeline/recipes/sip/nodes/imager_source_finding.py b/CEP/Pipeline/recipes/sip/nodes/imager_source_finding.py index 07612e989918c527e7901ab3707d7a241c6ee6bd..464fb940c4a1dfc6bc009cb2f40b2c8197f91ce5 100644 --- a/CEP/Pipeline/recipes/sip/nodes/imager_source_finding.py +++ b/CEP/Pipeline/recipes/sip/nodes/imager_source_finding.py @@ -8,6 +8,7 @@ from lofarpipe.support.lofarnode import LOFARnodeTCP from lofarpipe.support.pipelinelogging import CatchLog4CPlus from lofarpipe.support.utilities import catch_segfaults +from lofarpipe.support.utilities import create_directory class imager_source_finding(LOFARnodeTCP): @@ -53,6 +54,10 @@ class imager_source_finding(LOFARnodeTCP): """ + #****************************************************************** + # 0. Create the directories used in this recipe + create_directory(working_directory) + import lofar.bdsm as bdsm#@UnresolvedImport self.logger.info("Starting imager_source_finding") self.environment.update(environment) @@ -92,6 +97,9 @@ class imager_source_finding(LOFARnodeTCP): pass #do nothing bdsm_parameters[key] = parameter_value + # pybdsm needs its filename here, to derive the log location + bdsm_parameters["filename"] = input_image_local + # ***************************************************************** # 3. Start pybdsm @@ -99,8 +107,7 @@ class imager_source_finding(LOFARnodeTCP): "Starting sourcefinder bdsm on {0} using parameters:".format( input_image_local)) self.logger.debug(repr(bdsm_parameters)) - img = bdsm.process_image(bdsm_parameters, - filename = input_image_local, frequency = frequency) + img = bdsm.process_image(bdsm_parameters, frequency = frequency) # Always export the catalog img.write_catalog( diff --git a/CEP/Pipeline/recipes/sip/nodes/long_baseline.py b/CEP/Pipeline/recipes/sip/nodes/long_baseline.py index cea1e3c7752e639ae6f12cd68feb3268ecb4b4a1..282255861a791d3a797f41745a0409464aa55801 100644 --- a/CEP/Pipeline/recipes/sip/nodes/long_baseline.py +++ b/CEP/Pipeline/recipes/sip/nodes/long_baseline.py @@ -45,17 +45,21 @@ class long_baseline(LOFARnodeTCP): ndppp_executable, output_measurement_set, subbandgroups_per_ms, subbands_per_subbandgroup, ms_mapfile, asciistat_executable, statplot_executable, msselect_executable, - rficonsole_executable, add_beam_tables, final_output_path): + rficonsole_executable, add_beam_tables, globalfs, final_output_path): """ Entry point for the node recipe """ self.environment.update(environment) + self.globalfs = globalfs with log_time(self.logger): input_map = DataMap.load(ms_mapfile) #****************************************************************** # I. Create the directories used in this recipe create_directory(processed_ms_dir) + create_directory(working_dir) + create_directory(os.path.dirname(output_measurement_set)) + create_directory(os.path.dirname(final_output_path)) # time slice dir_to_remove: assure empty directory: Stale data # is problematic for dppp @@ -194,9 +198,10 @@ class long_baseline(LOFARnodeTCP): command = ["rsync", "-r", "{0}:{1}".format( input_item.host, input_item.file), "{0}".format(processed_ms_dir)] - if input_item.host == "localhost": - command = ["cp", "-r", "{0}".format(input_item.file), - "{0}".format(processed_ms_dir)] + if self.globalfs or input_item.host == "localhost": + # symlinking is enough + command = ["ln", "-sf", "{0}".format(input_item.file), + "-t", "{0}".format(processed_ms_dir)] self.logger.debug("executing: " + " ".join(command)) @@ -497,12 +502,6 @@ class long_baseline(LOFARnodeTCP): table = pt.table(output_measurement_set) - try: - os.makedirs(os.path.dirname(final_output_path)) - except: - pass # do nothing, the path already exists, we can output to this - # location - table.copy(final_output_path, deep=True) diff --git a/CEP/Pipeline/recipes/sip/pipeline.cfg.CEP4.tmpl b/CEP/Pipeline/recipes/sip/pipeline.cfg.CEP4.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..88bf3b935c008bd2143cf671104f49b892ec6ba6 --- /dev/null +++ b/CEP/Pipeline/recipes/sip/pipeline.cfg.CEP4.tmpl @@ -0,0 +1,73 @@ +# This pipeline.cfg is used on CEP4, to run jobs through Docker and SLURM. + +[DEFAULT] +lofarroot = /opt/lofar +casaroot = /opt/casacore +pyraproot = /opt/python-casacore/pyrap +hdf5root = +wcsroot = /opt/wcslib +pythonpath = /opt/lofar/lib/python2.7/site-packages +# runtime dir is a global FS (nfs, lustre) to exchange small files (parsets, vds, map files, etc) +runtime_directory = /data/share/pipeline +recipe_directories = [%(pythonpath)s/lofarpipe/recipes] +# working dir is the local dir in which input/output dataproducts reside +working_directory = /data/scratch/pipeline +task_files = [%(lofarroot)s/share/pipeline/tasks.cfg, %(lofarroot)s/share/pipeline/tasks.cfg.CEP4] + +[layout] +job_directory = %(runtime_directory)s/%(job_name)s + +[cluster] +clusterdesc = %(lofarroot)s/share/local.clusterdesc + +[deploy] +engine_ppath = %(pythonpath)s:%(pyraproot)s/lib:/opt/cep/pythonlibs/lib/python/site-packages +engine_lpath = %(lofarroot)s/lib:%(casaroot)s/lib:%(pyraproot)s/lib:%(hdf5root)s/lib:%(wcsroot)s/lib + +[logging] +log_file = %(runtime_directory)s/%(job_name)s/logs/%(start_time)s/pipeline.log +xml_stat_file = %(runtime_directory)s/%(job_name)s/logs/%(start_time)s/statistics.xml + +[feedback] +# Method of providing feedback to LOFAR. +# Valid options: +# messagebus Send feedback and status using LCS/MessageBus +# none Do NOT send feedback and status +method = messagebus + +# Report final state on the message bus? (yes for CEP2 using "startPython.sh", no for CEP4 using "runPipeline.sh") +send_status = no + +[remote] +method = custom_cmdline +globalfs = yes + +# We take the following path to start a remote container: +# +# [container] --SRUN--> [worker node] --DOCKER--> [container] +# +# This path is needed because running SRUN from within the container needs a lot of cluster-specific infra +# (SLURM config files, Munge keys, correct Munge user ID, munged). +# +# Throughout this path, we maintain: +# * userid, which is set to the user that started the master script container (-u `id -u`) +# * environment (sudo -p, docker -e K=V, etc) +# * pseudo-tty to prevent buffering logs (ssh -tt, docker -t) + +# +# host -> worker node: srun ... +# (Add -w {host} for systems that do not have a global file system, to force job +# execution on the host that contains the data) +# +# worker node -> container: docker run ... +# Starts the container on the worker node, with pretty much the same parameters as the master container: +# +# --rm: don't linger resources when the container exits +# -u (uid): set the user to run as (the calling user) +# -h {host}: set container hostname (for easier debugging) (doesnt work yet, using --net=host instead) +# -v /data:/data: map CEP4's global fs. This includes %(working_directory)s and %(runtime_directory)s so those do not have to be mapped as well. +# +# /bin/bash -c +# +# Required because the pipeline framework needs some bash functionality in the commands it starts. +cmdline = srun --exclusive --ntasks=1 --cpus-per-task={nr_cores} --jobid={slurm_job_id} --job-name={job_name} /data/bin/docker-run-slurm.sh --rm -u {uid} -v /data:/data --net=host {docker_env} lofar-pipeline:${LOFAR_TAG} {command} diff --git a/CEP/Pipeline/recipes/sip/pipeline.cfg.in b/CEP/Pipeline/recipes/sip/pipeline.cfg.in index 01ae7714f0d743792739ace5dbea522424cf8b78..d4b175ad9e545394dac4ff7a30a4e7c13ab14fb5 100644 --- a/CEP/Pipeline/recipes/sip/pipeline.cfg.in +++ b/CEP/Pipeline/recipes/sip/pipeline.cfg.in @@ -4,6 +4,7 @@ casaroot = @CASACORE_ROOT_DIR@ pyraproot = @PYRAP_ROOT_DIR@ hdf5root = $ENV{HDF5_ROOT} wcsroot = @WCSLIB_ROOT_DIR@ +aoflaggerroot=@AOFLAGGER_ROOT_DIR@ pythonpath = @PYTHON_INSTALL_DIR@ runtime_directory = %(lofarroot)s/var/run/pipeline recipe_directories = [%(pythonpath)s/lofarpipe/recipes] diff --git a/CEP/Pipeline/recipes/sip/pipeline.cfg.thead01.cep4 b/CEP/Pipeline/recipes/sip/pipeline.cfg.thead01.cep4 deleted file mode 100644 index 62ad298207d88a02f6ed3c1c416a505127967774..0000000000000000000000000000000000000000 --- a/CEP/Pipeline/recipes/sip/pipeline.cfg.thead01.cep4 +++ /dev/null @@ -1,89 +0,0 @@ -# This pipeline.cfg was used on the CEP4 test system to show how Docker and SLURM -# can be used to distribute and run jobs. -# -# Is called as follows (inside a SLURM job allocation, f.e. "salloc -N 2"). Replace directory names -# where needed: -# -# # Create this on every node -# CONFIGDIR=/globalhome/mol/regression_test -# HOSTS=(`scontrol show hostnames $SLURM_JOB_NODELIST`) -# -# # $CONFIGDIR is local, and ocntains both the working_dir (for input/output data) and the config files -# # /share is a shared disk for the vds files, map files, logs. -# -# docker run --rm -e SLURM_JOB_ID=${SLURM_JOB_ID} -e LUSER=${UID} -v $HOME/.ssh:/home/lofar/.ssh:ro -v $CONFIGDIR:$CONFIGDIR -v /shared:/shared --net=host lofar-patched /bin/bash -c "\"$EXTRA_CMDS;python $CONFIGDIR/regression_test_runner.py preprocessing_pipeline --pipelinecfg $CONFIGDIR/pipeline.cfg --testdata $CONFIGDIR/data --computehost1 ${HOSTS[0]} --computehost2 ${HOSTS[1]} --workdir $CONFIGDIR/working_dir --rundir $CONFIGDIR/rundir\"" - - -[DEFAULT] -lofarroot = /opt/lofar -casaroot = /opt/casacore -pyraproot = /opt/python-casacore/pyrap -hdf5root = -wcsroot = /opt/wcslib -pythonpath = /opt/lofar/lib/python2.7/site-packages -# runtime dir is a global FS (nfs, lustre) to exchange small files (parsets, vds, map files, etc) -runtime_directory = /shared/mol/regression_test/rundir -recipe_directories = [%(pythonpath)s/lofarpipe/recipes] -# working dir is the local dir in which input/output dataproducts reside -working_directory = /globalhome/mol/regression_test/working_dir -task_files = [%(lofarroot)s/share/pipeline/tasks.cfg] - -[layout] -job_directory = %(runtime_directory)s/%(job_name)s - -[cluster] -clusterdesc = %(lofarroot)s/share/local.clusterdesc - -[deploy] -engine_ppath = %(pythonpath)s:%(pyraproot)s/lib:/opt/cep/pythonlibs/lib/python/site-packages -engine_lpath = %(lofarroot)s/lib:%(casaroot)s/lib:%(pyraproot)s/lib:%(hdf5root)s/lib:%(wcsroot)s/lib - -[logging] -log_file = %(runtime_directory)s/%(job_name)s/logs/%(start_time)s/pipeline.log -xml_stat_file = %(runtime_directory)s/%(job_name)s/logs/%(start_time)s/statistics.xml - -[feedback] -# Method of providing feedback to LOFAR. -# Valid options: -# messagebus Send feedback and status using LCS/MessageBus -# none Do NOT send feedback and status -method = none - -[docker] -image = lofar-patched - -[remote] -#method = slurm_srun_cep3 -#method = ssh_docker -method = custom_cmdline -max_per_node = 1 - -# We take the following path to start a remote container: -# -# [container] --SSH--> [host] --SRUN--> [worker node] --DOCKER--> [container] -# -# This path is needed because running SRUN from within the container needs a lot of cluster-specific infra -# (SLURM config files, Munge keys, correct Munge user ID, munged). -# -# Throughout this path, we maintain: -# * userid, which is set to the user that started the master script container (-e LUSER=`id -u`) -# * environment (sudo -p, docker -e K=V, etc) -# * pseudo-tty to prevent buffering logs (ssh -tt, docker -t) - -# -# host -> worker node: srun -w {host} -N 1 -n 1 --jobid={slurm_job_id} -# Needs the specific host, because the test system does not have a global file system. -# -# worker node -> container: docker run -t --rm -e LUSER={uid} -w g -v /home/mol/.ssh:/home/lofar/.ssh:ro -v /globalhome/mol/regression_test:/globalhome/mol/regression_test -v /shared:/shared --net=host {docker_image} -# Starts the container on the worker node, with pretty much the same parameters as the master container: -# -# --rm: don't linger resources when the container exits -# -e LUSER=(uid): set the user to run as (the calling user) -# -h {host}: set container hostname (for easier debugging) (doesnt work yet, using --net=host instead) -# -v: map the directories for input/output data, shared storage (parsets, vds files, etc) -# -# /bin/bash -c -# -# Required because the pipeline framework needs some bash functionality in the commands it starts. -#cmdline = ssh -n -tt -x localhost srun -w {host} -N 1 -n 1 --jobid={slurm_job_id} docker run -t --rm -e LUSER={uid} -w g -v /home/mol/.ssh:/home/lofar/.ssh:ro -v /globalhome/mol/regression_test:/globalhome/mol/regression_test -v /shared:/shared --net=host {docker_image} /bin/bash -c -cmdline = ssh -n -tt -x localhost srun -w {host} -N 1 -n 1 --jobid={slurm_job_id} docker run -t --rm -e LUSER={uid} -v %(runtime_directory)s:%(runtime_directory)s -v %(working_directory)s:%(working_directory)s --net=host {docker_image} /bin/bash -c "\"{command}\"" diff --git a/CEP/Pipeline/recipes/sip/tasks.cfg.CEP4.in b/CEP/Pipeline/recipes/sip/tasks.cfg.CEP4.in new file mode 100644 index 0000000000000000000000000000000000000000..bc8e4f1ef5e58b448a2f026937c7047f049e9964 --- /dev/null +++ b/CEP/Pipeline/recipes/sip/tasks.cfg.CEP4.in @@ -0,0 +1,61 @@ +[ndppp] +nproc = 0 +nthreads = 2 + +[setupparmdb] +nproc = 0 + +[setupsourcedb] +nproc = 0 + +[vdsmaker] +nproc = 0 + +[rficonsole] +nproc = 0 + +[imager_prepare] +nthreads = 10 + +[long_baseline] +nproc = 0 +rficonsole_executable = /opt/aoflagger/bin/aoflagger +nthreads = 10 + +[dppp] +max_per_node = 0 +nthreads = 2 + +[awimager] +max_per_node = 0 +nthreads = 10 + +[rficonsole] +executable = /opt/aoflagger/bin/aoflagger +max_per_node = 0 +nthreads = 10 + +[imager_prepare] +rficonsole_executable = /opt/aoflagger/bin/aoflagger +nthreads = 10 + +[imager_bbs] +nthreads = 10 + +[bbs_reducer] +nthreads = 10 + +[executable_args] +nthreads = 10 + +[casapy-imager] +nthreads = 10 + +[pythonplugin] +nthreads = 10 + +[python-calibrate-stand-alone] +nthreads = 10 + +[calibrate-stand-alone] +nthreads = 10 diff --git a/CEP/Pipeline/recipes/sip/tasks.cfg.in b/CEP/Pipeline/recipes/sip/tasks.cfg.in index e2968e1bc5b9c6327b0b130890b47450b8f2657f..dcc46ffb7be3487a2cf3bd2dd21c223828ee8d91 100644 --- a/CEP/Pipeline/recipes/sip/tasks.cfg.in +++ b/CEP/Pipeline/recipes/sip/tasks.cfg.in @@ -50,7 +50,7 @@ mapfile = %(runtime_directory)s/%(job_name)s/mapfiles/instrument.mapfile [rficonsole] recipe = rficonsole -executable = %(lofarroot)s/bin/rficonsole +executable = %(aoflaggerroot)s/bin/aoflagger [get_metadata] recipe = get_metadata @@ -61,7 +61,8 @@ ndppp_exec = %(lofarroot)s/bin/NDPPP asciistat_executable = %(lofarroot)s/bin/asciistats.py statplot_executable = %(lofarroot)s/bin/statsplot.py msselect_executable = %(casaroot)s/bin/msselect -rficonsole_executable = %(lofarroot)s/bin/rficonsole +rficonsole_executable = %(aoflaggerroot)s/bin/aoflagger +nthreads = 8 [long_baseline] recipe = long_baseline @@ -69,12 +70,14 @@ ndppp_exec = %(lofarroot)s/bin/NDPPP asciistat_executable = %(lofarroot)s/bin/asciistats.py statplot_executable = %(lofarroot)s/bin/statsplot.py msselect_executable = %(casaroot)s/bin/msselect -rficonsole_executable = %(lofarroot)s/bin/rficonsole +rficonsole_executable = %(aoflaggerroot)s/bin/aoflagger nproc = 1 +nthreads = 8 [imager_awimager] recipe = imager_awimager executable = %(lofarroot)s/bin/awimager +nthreads = 8 [imager_create_dbs] recipe = imager_create_dbs @@ -84,6 +87,7 @@ makesourcedb_path = %(lofarroot)s/bin/makesourcedb [imager_bbs] recipe = imager_bbs bbs_executable = %(lofarroot)s/bin/bbs-reducer +nthreads = 8 [imager_source_finding] recipe = imager_source_finding @@ -104,6 +108,7 @@ parset = %(runtime_directory)s/%(job_name)s/parsets/bbs.parset instrument_mapfile = %(runtime_directory)s/%(job_name)s/mapfiles/instrument.mapfile sky_mapfile = %(runtime_directory)s/%(job_name)s/mapfiles/sky.mapfile data_mapfile = %(runtime_directory)s/%(job_name)s/mapfiles/bbs.mapfile +nthreads = 8 [selfcal_awimager] recipe = selfcal_awimager @@ -134,6 +139,7 @@ inplace = False outputsuffixes = [] parsetasfile = False #args_format=gnu +nthreads = 8 [casapy-imager] recipe = executable_args @@ -141,20 +147,24 @@ parsetasfile = True #executable = /path/to/your/casapy/bin/casa outputsuffixes = [flux,image,model,residual,psf] nodescript = executable_casa +nthreads = 8 [pythonplugin] recipe = executable_args nodescript = python_plugin +nthreads = 8 [python-calibrate-stand-alone] recipe=executable_args nodescript=calibrate-stand-alone executable=%(lofarroot)s/bin/bbs-reducer parsetasfile=True +nthreads = 8 [calibrate-stand-alone] recipe = executable_args executable = %(lofarroot)s/bin/calibrate-stand-alone +nthreads = 8 [dppp] recipe = executable_args @@ -163,6 +173,7 @@ executable = %(lofarroot)s/bin/NDPPP outputsuffixes = [] args_format=lofar outputkey=msout +nthreads = 8 [awimager] recipe = executable_args @@ -172,7 +183,9 @@ outputsuffixes = [.model, .model.corr, .residual, .residual.corr, .restored, .re max_per_node = 1 args_format=lofar outputkey=image +nthreads = 8 [rficonsole] recipe = executable_args executable = %(lofarroot)s/bin/rficonsole +nthreads = 8 diff --git a/CEP/Pipeline/test/support/output_stderr_stdout.sh b/CEP/Pipeline/test/support/output_stderr_stdout.sh new file mode 100755 index 0000000000000000000000000000000000000000..31b86045230847fe7844cf4a41da656a6e49e622 --- /dev/null +++ b/CEP/Pipeline/test/support/output_stderr_stdout.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Spam stderr, but not enough to fill a 4k buffer. +# Reading from stderr should not block. +for i in `seq 1 10`; do + echo e${i}e 1>&2 +done + +# Now spam stdout, filling any buffer. +# If reading from stderr blocks in the previuos loop, +# we can't finish this loop. +for i in `seq 1 4096`; do + echo o${i}o o${i}o o${i}o o${i}o o${i}o o${i}o +done diff --git a/CEP/Pipeline/test/support/subprocessgroup_test.py b/CEP/Pipeline/test/support/subprocessgroup_test.py index 290be2967ce8fc88696b6307ebc13402a33e589e..e5bd35694f602d807b370824ec1ee5650625e438 100644 --- a/CEP/Pipeline/test/support/subprocessgroup_test.py +++ b/CEP/Pipeline/test/support/subprocessgroup_test.py @@ -25,6 +25,21 @@ class SubProcessGroupTest(unittest.TestCase): """ + def test_alternating_output(self): + process_group = SubProcessGroup(polling_interval=10) + + # print a lot of numbers + cmd = '%s/output_stderr_stdout.sh' % (os.path.dirname(__file__) or ".",) + start_time = time.time() + + # Start it multiple times + for idx in range(2): + process_group.run(cmd) + + process_group.wait_for_finish() + end_time = time.time() + self.assertTrue((end_time - start_time) < 10) + def test_limit_number_of_proc(self): process_group = SubProcessGroup(polling_interval=1) @@ -42,19 +57,27 @@ class SubProcessGroupTest(unittest.TestCase): process_group.wait_for_finish() end_time = time.time() self.assertTrue((end_time - start_time) > 3) - + + + def test_fd_bigger_than_1024(self): + process_group = SubProcessGroup(polling_interval=1, max_concurrent_processes=1000) + + cmd = "sleep 2" + for idx in range(513): # each process uses 2 fds, so we only need 513 processes to ensure fds > 1024 + process_group.run(cmd) + + process_group.wait_for_finish() + def test_start_without_jobs(self): - process_group = SubProcessGroup(polling_interval=1) + process_group = SubProcessGroup(polling_interval=10) - # wait for 5 seconds start_time = time.time() - process_group.wait_for_finish() end_time = time.time() # The wait should complete without a polling interfal - self.assertTrue((end_time - start_time) < 1) + self.assertTrue((end_time - start_time) < 10) if __name__ == '__main__': import xmlrunner diff --git a/CEP/PyBDSM/doc/source/conf.py b/CEP/PyBDSM/doc/source/conf.py index a7508205f40def162a2ac03200bdf5f19bc5d46c..fe682b9ede0967deea7cb7d3e2c0e4531fa95185 100644 --- a/CEP/PyBDSM/doc/source/conf.py +++ b/CEP/PyBDSM/doc/source/conf.py @@ -50,7 +50,7 @@ copyright = u'2016, David Rafferty and Niruj Mohan' # The short X.Y version. version = '1.8' # The full version, including alpha/beta/rc tags. -release = '1.8.6' +release = '1.8.7' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -248,7 +248,7 @@ texinfo_documents = [ epub_title = u'PyBDSM' epub_author = u'David Rafferty and Niruj Mohan' epub_publisher = u'David Rafferty and Niruj Mohan' -epub_copyright = u'2013, David Rafferty and Niruj Mohan' +epub_copyright = u'2016, David Rafferty and Niruj Mohan' # The language of the text. It defaults to the language option # or en if the language is not set. diff --git a/CEP/PyBDSM/doc/source/whats_new.rst b/CEP/PyBDSM/doc/source/whats_new.rst index 7d3ac6267c03e72ada221da7302ae4e20405a610..bd0e282e02c41d8ba49867718d5163612d06a584 100644 --- a/CEP/PyBDSM/doc/source/whats_new.rst +++ b/CEP/PyBDSM/doc/source/whats_new.rst @@ -4,13 +4,16 @@ What's New ********** +Version 1.8.7 (2016/06/10) + + * Fix to bug that caused incorrect output images when input image was not square. + Version 1.8.6 (2016/01/20) * Fix to bug that caused incorrect island mask when two islands are very close together. * Fix to bug that caused crash when image is masked and the ``src_ra_dec`` option is used. - Version 1.8.5 (2015/11/30): * Fix to bug in ``export_image`` that resulted in incorrect output image when both ``trim_box`` and ``pad_image`` were used. diff --git a/CEP/PyBDSM/src/c++/MGFunction1.cc b/CEP/PyBDSM/src/c++/MGFunction1.cc index b37c5a41018aa5e0be1fca92111672446f61d781..9aacf8e516e234c9be727e9c68d5b6a5ac316eea 100644 --- a/CEP/PyBDSM/src/c++/MGFunction1.cc +++ b/CEP/PyBDSM/src/c++/MGFunction1.cc @@ -197,7 +197,7 @@ boost::python::tuple MGFunction::py_find_peak() int x1 = mm_data[pidx].x1; int x2 = mm_data[pidx].x2; - return boost::python::make_tuple(peak, make_tuple(x1, x2)); + return boost::python::make_tuple(peak, boost::python::make_tuple(x1, x2)); } diff --git a/CEP/PyBDSM/src/python/_version.py b/CEP/PyBDSM/src/python/_version.py index 6b965579414c14023b5d24f5408d2c8e6c15fb72..5b2697fe84110e0afd85337e17ec4df5687d0666 100644 --- a/CEP/PyBDSM/src/python/_version.py +++ b/CEP/PyBDSM/src/python/_version.py @@ -9,7 +9,7 @@ adding to the changelog will naturally do this. """ # Version number -__version__ = '1.8.6' +__version__ = '1.8.7' # Store svn Revision number. For this to work, one also needs to do: # @@ -27,6 +27,11 @@ def changelog(): PyBDSM Changelog. ----------------------------------------------------------------------- + 2016/06/10 - Version 1.8.7 + + 2016/06/10 - Fix to bug that caused incorrect output images when input + image was not square. + 2016/01/20 - Version 1.8.6 2016/01/15 - Fix to bug that caused incorrect island mask when two diff --git a/CEP/PyBDSM/src/python/functions.py b/CEP/PyBDSM/src/python/functions.py index 358f6b7f780468e3eda9c28126033f5f249d2ae0..77d2c27028add9fe248ea63745d6e7e2211a68c6 100755 --- a/CEP/PyBDSM/src/python/functions.py +++ b/CEP/PyBDSM/src/python/functions.py @@ -1432,8 +1432,8 @@ def write_image_to_file(use, filename, image, img, outdir=None, return temp_im = make_fits_image(N.transpose(image), wcs_obj, img.beam, img.frequency, img.equinox, telescope, xmin=xmin, ymin=ymin, - is_mask=is_mask, shape=(img.shape[1], img.shape[0], image.shape[0], - image.shape[1])) + is_mask=is_mask, shape=(img.shape[1], img.shape[0], image.shape[1], + image.shape[0])) if use == 'rap': outfile = outdir + filename + '.fits' else: diff --git a/CMake/FindNumpy.cmake b/CMake/FindNumpy.cmake index 2e00bf677f49323de5ee8d9847144576ad12e065..4fd18a40704a5bf7d676601ac225ef7b18540177 100644 --- a/CMake/FindNumpy.cmake +++ b/CMake/FindNumpy.cmake @@ -59,7 +59,7 @@ if(NOT NUMPY_FOUND) endif(NOT NUMPY_INCLUDE_DIR) if(NOT NUMPY_MULTIARRAY_LIBRARY) - find_library(NUMPY_MULTIARRAY_LIBRARY multiarray + find_library(NUMPY_MULTIARRAY_LIBRARY NAME multiarray multiarray.${CMAKE_LIBRARY_ARCHITECTURE} HINTS ${NUMPY_PATH} PATH_SUFFIXES core) endif(NOT NUMPY_MULTIARRAY_LIBRARY) @@ -171,9 +171,9 @@ macro (add_f2py_module _name) # Define the command to generate the Fortran to Python interface module. The # output will be a shared library that can be imported by python. add_custom_command(OUTPUT ${_name}.so - COMMAND ${F2PY_EXECUTABLE} --quiet -m ${_name} -h ${_name}.pyf + COMMAND /usr/bin/env -u LDFLAGS ${F2PY_EXECUTABLE} --quiet -m ${_name} -h ${_name}.pyf --include_paths ${_inc_paths} --overwrite-signature ${_abs_srcs} - COMMAND ${F2PY_EXECUTABLE} --quiet -m ${_name} -c ${_name}.pyf + COMMAND /usr/bin/env -u LDFLAGS ${F2PY_EXECUTABLE} --quiet -m ${_name} -c ${_name}.pyf ${_fcompiler_opts} ${_inc_opts} ${_abs_srcs} DEPENDS ${_srcs} COMMENT "[F2PY] Building Fortran to Python interface module ${_name}") diff --git a/CMake/LofarMacros.cmake b/CMake/LofarMacros.cmake index a0de05215df2db71f4d782062643fc5b569851d1..6a34132d08a14106e2626f79b37204687b4b7961 100644 --- a/CMake/LofarMacros.cmake +++ b/CMake/LofarMacros.cmake @@ -7,6 +7,7 @@ # lofar_add_library(name) # lofar_add_sbin_program(name) # lofar_add_sbin_scripts([name1 [name2 ..]]) +# lofar_add_sysconf_files([name1 [name2 ..]]) # lofar_add_test(name) # lofar_create_target_symlink(target symlink) # lofar_join_arguments(var) @@ -163,6 +164,26 @@ if(NOT DEFINED LOFAR_MACROS_INCLUDED) endmacro(lofar_add_sbin_scripts) + # -------------------------------------------------------------------------- + # lofar_add_sysconf_files([name1 [name2 ..]]) + # + # Add system configuration files (read-only single machine data) that need + # to be installed into the <prefix>/etc directory. Also create a symbolic + # link in <build-dir>/etc to each of these files. The file names may contain + # a relative(!) path. + # -------------------------------------------------------------------------- + macro(lofar_add_sysconf_files) + foreach(_name ${ARGN}) + get_filename_component(_path ${_name} PATH) + get_filename_component(_abs_name ${_name} ABSOLUTE) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/etc/${_path}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + ${_abs_name} ${CMAKE_BINARY_DIR}/etc/${_name}) + install(FILES ${_name} DESTINATION etc/${_path}) + endforeach(_name ${ARGN}) + endmacro(lofar_add_sysconf_files) + + # -------------------------------------------------------------------------- # lofar_add_test(name [source ...] [DEPENDS depend ...]) # @@ -213,6 +234,10 @@ if(NOT DEFINED LOFAR_MACROS_INCLUDED) # use of the generator expression $<TARGET_FILE>. # -------------------------------------------------------------------------- macro(lofar_create_target_symlink _target _symlink) + if(NOT TARGET ${_target}) + message(SEND_ERROR + "Cannot create symbolic link to non-existing target ${_target}") + endif(NOT TARGET ${_target}) if(POLICY CMP0026) set(_location $<TARGET_FILE:${_target}>) else(POLICY CMP0026) diff --git a/CMake/LofarPackageList.cmake b/CMake/LofarPackageList.cmake index 5939efc2a0c4b325eb4f14dfd8c9f68049a439e0..5fee65dd22d56785fdea2c0921825dc8ce2851d6 100644 --- a/CMake/LofarPackageList.cmake +++ b/CMake/LofarPackageList.cmake @@ -1,7 +1,7 @@ # - Create for each LOFAR package a variable containing the absolute path to # its source directory. # -# Generated by gen_LofarPackageList_cmake.sh at ma apr 4 12:02:12 CEST 2016 +# Generated by gen_LofarPackageList_cmake.sh at Fri Sep 9 08:05:41 UTC 2016 # # ---- DO NOT EDIT ---- # @@ -37,8 +37,8 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) set(PythonDPPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/PythonDPPP) set(DPPP_AOFlag_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/DPPP_AOFlag) set(SPW_Combine_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/SPWCombine) - set(AOFlagger_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/AOFlagger) set(LofarFT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Imager/LofarFT) + set(AOFlagger_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/DP3/AOFlagger) set(AWImager2_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/Imager/AWImager2) set(Laps-GRIDInterface_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/LAPS/GRIDInterface) set(Laps-ParsetCombiner_SOURCE_DIR ${CMAKE_SOURCE_DIR}/CEP/LAPS/ParsetCombiner) @@ -121,6 +121,7 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) set(Deployment_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Deployment) set(Navigator2_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Navigator2) set(MACTools_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Tools) + set(MAC_Services_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Services) set(PVSS_Datapoints_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Deployment/data/PVSS) set(OTDB_Comps_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Deployment/data/OTDB) set(StaticMetaData_SOURCE_DIR ${CMAKE_SOURCE_DIR}/MAC/Deployment/data/StaticMetaData) @@ -138,15 +139,18 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) set(OTDB_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTDB) set(OTB_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTB) set(OTDB_SQL_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTDB/sql) + set(QPIDInfrastructure_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/QPIDInfrastructure) set(Scheduler_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/Scheduler) set(SAS_Feedback_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/Feedback_Service) set(CleanupTool_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/CleanupTool) set(OTDB_Services_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTDB_Services) set(XML_generator_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/XML_generator) + set(DataManagementCommon_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/DataManagement/DataManagementCommon) + set(CleanupService_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/DataManagement/CleanupService) + set(StorageQueryService_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/DataManagement/StorageQueryService) set(MoMQueryService_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/MoM/MoMQueryService) set(jOTDB3_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTB/jOTDB3) set(OTB-Java_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/OTB/OTB) - set(QPIDInfrastructure_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/QPIDInfrastructure) set(RATaskSpecifiedService_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/RATaskSpecifiedService) set(RAtoOTDBTaskSpecificationPropagator_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator) set(ResourceAssigner_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/ResourceAssigner) @@ -157,6 +161,7 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) set(SystemStatusDatabase_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/SystemStatusDatabase) set(SystemStatusService_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/SystemStatusService) set(OTDBtoRATaskStatusPropagator_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator) + set(RAScripts_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SAS/ResourceAssignment/RAScripts) set(CCU_MAC_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/CCU_MAC) set(LCU_MAC_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/LCU_MAC) set(MCU_MAC_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/MCU_MAC) @@ -170,4 +175,6 @@ if(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) set(PVSS_DB_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/PVSS_DB) set(LAPS_CEP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/LAPS_CEP) set(RAServices_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/RAServices) + set(DataManagement_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/DataManagement) + set(Dragnet_SOURCE_DIR ${CMAKE_SOURCE_DIR}/SubSystems/Dragnet) endif(NOT DEFINED LOFAR_PACKAGE_LIST_INCLUDED) diff --git a/CMake/variants/CLANG.cmake b/CMake/variants/CLANG.cmake index 84a397c478f68233ae0d6fcda55ed520f6f23d35..4a4a3bc1976e152584b4192e2a2b34c12c874417 100644 --- a/CMake/variants/CLANG.cmake +++ b/CMake/variants/CLANG.cmake @@ -7,7 +7,7 @@ set(LOFAR_COMPILER_SUITES CLANG) # Build variants -set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3) +set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3 OPTARCH) # CLANG compiler suite set(CLANG_COMPILERS CLANG_C CLANG_CXX CLANG_Fortran CLANG_ASM) @@ -20,21 +20,27 @@ set(CLANG_C_FLAGS "-W -Wall -Wno-unknown-pragmas") set(CLANG_C_FLAGS_DEBUG "-g") set(CLANG_C_FLAGS_OPT "-g -O2") set(CLANG_C_FLAGS_OPT3 "-g -O3") -set(CLANG_CXX_FLAGS "-W -Wall -Woverloaded-virtual -Wno-unknown-pragmas") +set(CLANG_C_FLAGS_OPTARCH "-g -O3 -march=native") +set(CLANG_CXX_FLAGS "-W -Wall -Woverloaded-virtual -Wno-unknown-pragmas -stdlib=libc++") set(CLANG_CXX_FLAGS_DEBUG "-g") set(CLANG_CXX_FLAGS_OPT "-g -O2") set(CLANG_CXX_FLAGS_OPT3 "-g -O3") +set(CLANG_CXX_FLAGS_OPTARCH "-g -O3 -march=native") set(CLANG_EXE_LINKER_FLAGS) set(CLANG_EXE_LINKER_FLAGS_DEBUG) set(CLANG_EXE_LINKER_FLAGS_OPT) set(CLANG_EXE_LINKER_FLAGS_OPT3) +set(CLANG_EXE_LINKER_FLAGS_OPTARCH) set(CLANG_SHARED_LINKER_FLAGS) set(CLANG_SHARED_LINKER_FLAGS_DEBUG) set(CLANG_SHARED_LINKER_FLAGS_OPT) set(CLANG_SHARED_LINKER_FLAGS_OPT3) +set(CLANG_SHARED_LINKER_FLAGS_OPTARCH) set(CLANG_COMPILE_DEFINITIONS) set(CLANG_COMPILE_DEFINITIONS_DEBUG "-DLOFAR_DEBUG -DENABLE_DBGASSERT -DENABLE_TRACER") set(CLANG_COMPILE_DEFINITIONS_OPT) set(CLANG_COMPILE_DEFINITIONS_OPT3 "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") +set(CLANG_COMPILE_DEFINITIONS_OPTARCH + "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") diff --git a/CMake/variants/GNU.cmake b/CMake/variants/GNU.cmake index 24fde524c5d44d72c905887f84ddbf336523b558..0077dcd50ac9e56348543c58dcbeb50154e60190 100644 --- a/CMake/variants/GNU.cmake +++ b/CMake/variants/GNU.cmake @@ -7,7 +7,7 @@ set(LOFAR_COMPILER_SUITES GNU) # Build variants -set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3) +set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3 OPTARCH) # GNU compiler suite set(GNU_COMPILERS GNU_C GNU_CXX GNU_Fortran GNU_ASM) @@ -20,21 +20,28 @@ set(GNU_C_FLAGS "-W -Wall -Wno-unknown-pragmas") set(GNU_C_FLAGS_DEBUG "-g") set(GNU_C_FLAGS_OPT "-g -O2") set(GNU_C_FLAGS_OPT3 "-g -O3") +set(GNU_C_FLAGS_OPTARCH "-g -O3 -march=native") set(GNU_CXX_FLAGS "-W -Wall -Woverloaded-virtual -Wno-unknown-pragmas") set(GNU_CXX_FLAGS_DEBUG "-g") set(GNU_CXX_FLAGS_OPT "-g -O2") set(GNU_CXX_FLAGS_OPT3 "-g -O3") +set(GNU_CXX_FLAGS_OPTARCH "-g -O3 -march=native") set(GNU_EXE_LINKER_FLAGS) set(GNU_EXE_LINKER_FLAGS_DEBUG) set(GNU_EXE_LINKER_FLAGS_OPT) set(GNU_EXE_LINKER_FLAGS_OPT3) +set(GNU_EXE_LINKER_FLAGS_OPTARCH) set(GNU_SHARED_LINKER_FLAGS) set(GNU_SHARED_LINKER_FLAGS_DEBUG) set(GNU_SHARED_LINKER_FLAGS_OPT) set(GNU_SHARED_LINKER_FLAGS_OPT3) +set(GNU_SHARED_LINKER_FLAGS_OPTARCH) set(GNU_COMPILE_DEFINITIONS) set(GNU_COMPILE_DEFINITIONS_DEBUG "-DLOFAR_DEBUG -DENABLE_DBGASSERT -DENABLE_TRACER") set(GNU_COMPILE_DEFINITIONS_OPT) set(GNU_COMPILE_DEFINITIONS_OPT3 "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") +set(GNU_COMPILE_DEFINITIONS_OPTARCH + "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") + diff --git a/CMake/variants/GNUCXX11.cmake b/CMake/variants/GNUCXX11.cmake index c2736c2e2df84e5555e55e9396a612cf99d81d42..643198e9b76d0a227b550cf08e3396401533df0b 100644 --- a/CMake/variants/GNUCXX11.cmake +++ b/CMake/variants/GNUCXX11.cmake @@ -7,34 +7,41 @@ set(LOFAR_COMPILER_SUITES GNUCXX11) # Build variants -set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3) +set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3 OPTARCH) -# GNUCXX11 compiler suite +# GNU compiler suite set(GNUCXX11_COMPILERS GNUCXX11_C GNUCXX11_CXX GNUCXX11_Fortran GNUCXX11_ASM) -set(GNUCXX11_C /usr/bin/gcc) # GNUCXX11 C compiler -set(GNUCXX11_CXX /usr/bin/g++) # GNUCXX11 C++ compiler -set(GNUCXX11_Fortran /usr/bin/gfortran) # GNUCXX11 Fortran compiler -set(GNUCXX11_ASM /usr/bin/gcc) # GNUCXX11 assembler +set(GNUCXX11_C /usr/bin/gcc) # GNU C compiler +set(GNUCXX11_CXX /usr/bin/g++) # GNU C++ compiler +set(GNUCXX11_Fortran /usr/bin/gfortran) # GNU Fortran compiler +set(GNUCXX11_ASM /usr/bin/gcc) # GNU assembler set(GNUCXX11_C_FLAGS "-W -Wall -Wno-unknown-pragmas") set(GNUCXX11_C_FLAGS_DEBUG "-g") set(GNUCXX11_C_FLAGS_OPT "-g -O2") set(GNUCXX11_C_FLAGS_OPT3 "-g -O3") -set(GNUCXX11_CXX_FLAGS "-std=c++11 -W -Wall -Woverloaded-virtual -Wno-unknown-pragmas") +set(GNUCXX11_C_FLAGS_OPTARCH "-g -O3 -march=native") +set(GNUCXX11_CXX_FLAGS "-std=gnu++11 -W -Wall -Woverloaded-virtual -Wno-unknown-pragmas") set(GNUCXX11_CXX_FLAGS_DEBUG "-g") set(GNUCXX11_CXX_FLAGS_OPT "-g -O2") set(GNUCXX11_CXX_FLAGS_OPT3 "-g -O3") +set(GNUCXX11_CXX_FLAGS_OPTARCH "-g -O3 -march=native") set(GNUCXX11_EXE_LINKER_FLAGS) set(GNUCXX11_EXE_LINKER_FLAGS_DEBUG) set(GNUCXX11_EXE_LINKER_FLAGS_OPT) set(GNUCXX11_EXE_LINKER_FLAGS_OPT3) +set(GNUCXX11_EXE_LINKER_FLAGS_OPTARCH) set(GNUCXX11_SHARED_LINKER_FLAGS) set(GNUCXX11_SHARED_LINKER_FLAGS_DEBUG) set(GNUCXX11_SHARED_LINKER_FLAGS_OPT) set(GNUCXX11_SHARED_LINKER_FLAGS_OPT3) +set(GNUCXX11_SHARED_LINKER_FLAGS_OPTARCH) set(GNUCXX11_COMPILE_DEFINITIONS) set(GNUCXX11_COMPILE_DEFINITIONS_DEBUG "-DLOFAR_DEBUG -DENABLE_DBGASSERT -DENABLE_TRACER") set(GNUCXX11_COMPILE_DEFINITIONS_OPT) set(GNUCXX11_COMPILE_DEFINITIONS_OPT3 "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") +set(GNUCXX11_COMPILE_DEFINITIONS_OPTARCH + "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") + diff --git a/CMake/variants/GNU_JUROPA.cmake b/CMake/variants/GNU_JUROPA.cmake index da072fe0f10ebbcda1eb9a8d12d4d1b2374da138..de3b884dea5758e12d1f5fba87d16b446dba2fb7 100644 --- a/CMake/variants/GNU_JUROPA.cmake +++ b/CMake/variants/GNU_JUROPA.cmake @@ -7,7 +7,7 @@ set(LOFAR_COMPILER_SUITES GNU_JUROPA) # Build variants -set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3) +set(LOFAR_BUILD_VARIANTS DEBUG OPT OPT3 OPTARCH) # GNU compiler suite set(GNU_JUROPA_COMPILERS GNU_C GNU_CXX GNU_Fortran GNU_ASM) @@ -20,21 +20,27 @@ set(GNU_C_FLAGS "-W -Wall -Wno-unknown-pragmas") set(GNU_C_FLAGS_DEBUG "-g") set(GNU_C_FLAGS_OPT "-g -O2") set(GNU_C_FLAGS_OPT3 "-g -O3") +set(GNU_C_FLAGS_OPTARCH "-g -O3 -march=native") set(GNU_CXX_FLAGS "-W -Wall -Woverloaded-virtual -Wno-unknown-pragmas") set(GNU_CXX_FLAGS_DEBUG "-g") set(GNU_CXX_FLAGS_OPT "-g -O2") set(GNU_CXX_FLAGS_OPT3 "-g -O3") +set(GNU_CXX_FLAGS_OPTARCH "-g -O3 -march=native") set(GNU_EXE_LINKER_FLAGS) set(GNU_EXE_LINKER_FLAGS_DEBUG) set(GNU_EXE_LINKER_FLAGS_OPT) set(GNU_EXE_LINKER_FLAGS_OPT3) +set(GNU_EXE_LINKER_FLAGS_OPTARCH) set(GNU_SHARED_LINKER_FLAGS) set(GNU_SHARED_LINKER_FLAGS_DEBUG) set(GNU_SHARED_LINKER_FLAGS_OPT) set(GNU_SHARED_LINKER_FLAGS_OPT3) +set(GNU_SHARED_LINKER_FLAGS_OPTARCH) set(GNU_COMPILE_DEFINITIONS) set(GNU_COMPILE_DEFINITIONS_DEBUG "-DLOFAR_DEBUG -DENABLE_DBGASSERT -DENABLE_TRACER") set(GNU_COMPILE_DEFINITIONS_OPT) set(GNU_COMPILE_DEFINITIONS_OPT3 "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") +set(GNU_COMPILE_DEFINITIONS_OPTARCH + "-DNDEBUG -DDISABLE_DEBUG_OUTPUT") diff --git a/CMake/variants/variants.Macbook-Pro b/CMake/variants/variants.Macbook-Pro deleted file mode 100644 index 832ac78323b1708cd604a4e10cc9d4205b27e480..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.Macbook-Pro +++ /dev/null @@ -1,17 +0,0 @@ -option(USE_LOG4CXX "log4cxx is used" OFF) -option(USE_SHMEM "No shmem" OFF) -option(USE_LOG4CPLUS "Do not use LOG4CPLUS" ON) -set(BOOST_ROOT_DIR /usr/local) -set(BOOST_INCLUDE_DIR /usr/local/include/boost) -set(HDF5_ROOT_DIR /usr/local/hdf5) -set(HDF5_INCLUDE_DIR /usr/local/hdf5/include) -set(PQ_LIBRARY /usr/local/pgsql/lib/libpq.a) -set(PQXX_LIBRARIES /usr/local/lib) -set(CASACORE_ROOT_DIR /usr/local) -set(CMAKE_INSTALL_PREFIX /opt/lofar) - -# GNU compiler suite -set(GNU_COMPILERS GNU_C GNU_CXX GNU_ASM) -set(GNU_C /opt/local/bin/gcc ) # GNU C compiler -set(GNU_CXX /opt/local/bin/g++ ) # GNU C++ compiler -set(GNU_ASM /opt/local/bin/gcc ) # GNU assembler diff --git a/CMake/variants/variants.b7015 b/CMake/variants/variants.b7015 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.b7015 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.buildhostcentos7 b/CMake/variants/variants.buildhostcentos7 new file mode 120000 index 0000000000000000000000000000000000000000..0c9019c0432464aaca3f677ce8c1104ce730f2fe --- /dev/null +++ b/CMake/variants/variants.buildhostcentos7 @@ -0,0 +1 @@ +variants.lcs157 \ No newline at end of file diff --git a/CMake/variants/variants.dop256 b/CMake/variants/variants.dop256 index 957ff6a3497d2614be41db8b504e536434317abf..6d8e7a91e430163cc4439395d7bdea643f881ba3 100644 --- a/CMake/variants/variants.dop256 +++ b/CMake/variants/variants.dop256 @@ -11,26 +11,7 @@ option(USE_CUDA "Use CUDA" ON) # Default search path for LOFAR deps (see CMake/variants/variants): /opt/lofar/external:/usr/local # The dirs thereunder must be lower case, e.g. unittest++/ or dal/ -# For the RPMs to make it work under CentOS 7, see https://support.astron.nl/lofar_issuetracker/issues/8161 - -# RHEL/CentOS 7 has log4cxx in the repo, but LOFAR log4cxx is dodgy, so install log4cplus from CentOS 6 rpm pkgs. -#option(USE_LOG4CPLUS "Use log4cplus" OFF) -#option(USE_LOG4CXX "Use log4cxx" ON) - -# RHEL/CentOS 7: blitz/blitz-devel N/A, so installed from two CentOS 6 rpm pkgs. (Idem for UnitTest++, but from Fedora 22 pkgs) -# blitz includes end up in /usr/include, but an arch specific one under /usr/lib64/blitz/include. -# Set the latter as INCLUDE_DIR, since /usr/include is already in the std search path. -set(BLITZ_INCLUDE_DIR /usr/lib64/blitz/include CACHE PATH "blitz include path") - -# RHEL/CentOS 7 has openmpi in /usr/lib64/openmpi and mpich in /usr/lib64/mpich -set(MPI_ROOT_DIR /usr/lib64/openmpi) - -# By default and on RHEL/CentOS 7, the GCC linker does not opt out overlinking. -# Make it so. It removes some ghost deps, but still leaves mysterious lib deps in place... -set(GNU_EXE_LINKER_FLAGS "-Wl,--as-needed") -set(GNU_SHARED_LINKER_FLAGS "-Wl,--as-needed") -set(CLANG_EXE_LINKER_FLAGS "-Wl,--as-needed") -set(CLANG_SHARED_LINKER_FLAGS "-Wl,--as-needed") +set(QPID_ROOT_DIR /opt/qpid) # Enable ccache symlinks to accelerate recompilation (/usr/bin/ccache). #set(GNU_C /usr/lib64/ccache/gcc) diff --git a/CMake/variants/variants.dop320 b/CMake/variants/variants.dop320 new file mode 100644 index 0000000000000000000000000000000000000000..e11707efd900a6574ec1f1adbb928c77e7f109b0 --- /dev/null +++ b/CMake/variants/variants.dop320 @@ -0,0 +1,2 @@ +# Fix for Debian (FindPython tends to settle on /usr/bin/python2.6, which is a "minimal" python install) +set(PYTHON_EXECUTABLE "/usr/bin/python2.7") diff --git a/CMake/variants/variants.dragnet b/CMake/variants/variants.dragnet index 1bcded7ad77699ac03dc07fd819f7e41917f38e0..de81cc7cd9acafe1f72d3333ae3bb267a878eac0 100644 --- a/CMake/variants/variants.dragnet +++ b/CMake/variants/variants.dragnet @@ -5,26 +5,32 @@ option(USE_MPI "Use MPI" ON) option(USE_CUDA "Use CUDA" ON) -# Specify versions, such that ABI incompat updates of these don't break already installed LOFAR binaries. Matters when we have to roll-back. -set(LOG4CPLUS_ROOT_DIR /opt/log4cplus-1.1.2) +# Default search path for LOFAR deps (see CMake/variants/variants): /opt/lofar/external:/usr/local +# The dirs thereunder must be lower case, e.g. unittest++/ or dal/ + +# For the RPMs to make it work under CentOS 7, see https://support.astron.nl/lofar_issuetracker/issues/8161 + +# Specify versioned paths, such that ABI incompat updates of these don't break already installed LOFAR binaries. Matters when we have to roll-back. +set(LOG4CPLUS_ROOT_DIR /opt/log4cplus-1.1.2) # RHEL/CentOS 7 has log4cxx in the repo, but LOFAR log4cxx is dodgy, so install log4cplus from CentOS 6 rpm pkgs. set(BLITZ_ROOT_DIR /opt/blitz-0.10) -set(CUDA_TOOLKIT_ROOT_DIR /opt/cuda-7.0) -set(CASACORE_ROOT_DIR /opt/casacore-2.0.3) -set(CASAREST_ROOT_DIR /opt/casarest) # pkg has no releases +set(CUDA_TOOLKIT_ROOT_DIR /opt/cuda-7.5) # libcuda.so on CentOS 7 w/ CUDA driver from ElRepo resides under /usr/lib64/nvidia/ +set(CASACORE_ROOT_DIR /opt/casacore-2.1.0) +set(CASAREST_ROOT_DIR /opt/casarest-1.4.1) set(CASA_ROOT_DIR /opt/casasynthesis) # for awimager2; pkg has no releases; it's a chunk of CASA, so var name is misleading, since it'll fail on the real CASA root dir set(DAL_ROOT_DIR /opt/lofardal-2.5.0) +set(AOFLAGGER_ROOT_DIR /opt/aoflagger-2.8.0) -# Avoid using unnecessary custom installed packages in /usr/local (NFS). -# This may clash with libs other deps (e.g. casacore) linked to, and deps on NFS (possible latency spike for COBALT). +# Force using /usr libs over custom (redundant) libs in /usr/local (NFS). +# They may clash with libs that other deps (e.g. casacore) linked to, and are on NFS (may cause latency spikes in COBALT). set(FFTW3_ROOT_DIR /usr) set(CFITSIO_ROOT_DIR /usr) set(GSL_ROOT_DIR /usr) # RHEL/CentOS 7 has openmpi in /usr/lib64/openmpi and mpich in /usr/lib64/mpich -set(MPI_ROOT_DIR /usr/lib64/openmpi) +set(MPI_ROOT_DIR /usr/lib64/openmpi) -# By default and on RHEL/CentOS 7, the GCC linker does not opt out overlinking. -# Make it so. It removes some ghost deps, but still leaves mysterious lib deps in place... +# By default and on RHEL/CentOS 7, the GCC linker does not opt out overlinking. To find overlinking: ldd -u -r <binary> +# Avoid overlinking with this (option must go before the libs). Still, some (e.g. mpi libs) are apparently "needed"(?) set(GNU_EXE_LINKER_FLAGS "-Wl,--as-needed") set(GNU_SHARED_LINKER_FLAGS "-Wl,--as-needed") set(CLANG_EXE_LINKER_FLAGS "-Wl,--as-needed") diff --git a/CMake/variants/variants.fs0 b/CMake/variants/variants.fs0 deleted file mode 100644 index bc655c68bf5c4612961fb6bde62c3aeacb33d705..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.fs0 +++ /dev/null @@ -1,22 +0,0 @@ -## -*- CMake -*- -## -## Host specific variants file. -## Settings in this file extend or override those in the global variants file. -## -## $Id: variants.fs5 25763 2013-07-25 09:33:20Z mol $ - -option(USE_LOG4CPLUS "Use Log4Cplus" ON) -option(USE_MPI "Use MPI" ON) - -set(CUDADRIVER_ROOT_DIR $ENV{CUDA_ROOT}) -set(CUDA_TOOLKIT_ROOT_DIR $ENV{CUDA_PATH}) -set(LOG4CPLUS_ROOT_DIR /home/mol/root) -set(CASACORE_ROOT_DIR /home/mol/root) -set(LIBSSH2_ROOT_DIR /home/mol/root) -set(DAL_ROOT_DIR /home/mol/root) -set(UNITTEST++_ROOT_DIR /home/mol/root/UnitTest++) - -# The file /usr/lib64/boost/BoostConfig.cmake in the package boost-devel-1.41 -# that ships with CentOS 6.3 seems to be broken. Setting Boost_NO_BOOST_CMAKE -# will ignore it. -set(Boost_NO_BOOST_CMAKE ON) diff --git a/CMake/variants/variants.fs5 b/CMake/variants/variants.fs5 deleted file mode 100644 index c8144443380e50cdd66f0fbe38272a744d26d2b2..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.fs5 +++ /dev/null @@ -1,47 +0,0 @@ -## -*- CMake -*- -## -## Host specific variants file. -## Settings in this file extend or override those in the global variants file. -## -## $Id$ - -option(USE_LOG4CPLUS "Use Log4Cplus" ON) -option(USE_MPI "Use MPI" ON) -# To build Cobalt, you must also enable USE_CUDA or USE_OPENCL (not both). - -set(CUDADRIVER_ROOT_DIR $ENV{CUDA_ROOT}) -set(CUDA_TOOLKIT_ROOT_DIR $ENV{CUDA_PATH}) -set(LOG4CPLUS_ROOT_DIR /home/jenkins/root) -set(CASACORE_ROOT_DIR /home/jenkins/root) -set(CASAREST_ROOT_DIR /home/jenkins/root) -set(CFITSIO_ROOT_DIR /home/jenkins/root) -set(PYRAP_ROOT_DIR /home/jenkins/root) -set(LIBSSH2_ROOT_DIR /home/jenkins/root) -set(BLITZ_ROOT_DIR /home/jenkins/root) -set(VALGRIND_ROOT_DIR /home/jenkins/root) # also in /usr, but ours has --with-mpicc=... (and includes, but we don't use them) -set(PQ_ROOT_DIR /usr/pgsql-8.4) -set(DAL_ROOT_DIR /home/jenkins/root) -set(UNITTEST++_ROOT_DIR /cm/shared/apps/UnitTest++) - -# To build MAC without deps on PVSS, pass -DBUILD_GCFPVSS=OFF. Run cmake a 2nd time to propagate. -#set(BUILD_GCFPVSS OFF) - -# The file /usr/lib64/boost/BoostConfig.cmake in the package boost-devel-1.41 -# that ships with CentOS 6.3 seems to be broken. Setting Boost_NO_BOOST_CMAKE -# will ignore it. -set(Boost_NO_BOOST_CMAKE ON) - - -# To use gcc 4.8.1 also use a newer boost. -#set(BOOST_ROOT_DIR /cm/shared/package/boost/1_54_0-gcc-4.8.1) -#set(Boost_ADDITIONAL_VERSIONS "1.54" "1.54.0") -#set(Boost_NO_SYSTEM_PATHS ON) - -# Enable gcc 4.8.1. If gcc does not have libbfd built (also) as a shared library, -# you get linker errors about static libs and -fPIC. Work around by passing to -# CMake -DUSE_BACKTRACE=OFF to get it into the CMake cache. -#set(GNU_C /cm/shared/package/gcc/4.8.1/bin/gcc) -#set(GNU_CXX /cm/shared/package/gcc/4.8.1/bin/g++) -#set(GNU_Fortran /cm/shared/package/gcc/4.8.1/bin/gfortran) -#set(GNU_ASM /cm/shared/package/gcc/4.8.1/bin/gcc) - diff --git a/CMake/variants/variants.gpu01 b/CMake/variants/variants.gpu01 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.gpu01 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.gpu1 b/CMake/variants/variants.gpu1 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.gpu1 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.head01 b/CMake/variants/variants.head01 new file mode 100644 index 0000000000000000000000000000000000000000..806bc3aaf79c80de6a41456283ddada86690dc96 --- /dev/null +++ b/CMake/variants/variants.head01 @@ -0,0 +1,3 @@ +set(CASACORE_ROOT_DIR /opt/casacore-latest) +set(DAL_ROOT_DIR /opt/DAL) + diff --git a/CMake/variants/variants.lcs157 b/CMake/variants/variants.lcs157 new file mode 100644 index 0000000000000000000000000000000000000000..026dfdfff6fa341372c2d19ee53fc07ac47fc187 --- /dev/null +++ b/CMake/variants/variants.lcs157 @@ -0,0 +1,15 @@ +# Station software and the like apparently cannot (yet) handle shared libs. +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) + +#set(ENV{JAVA_HOME} /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0) +#set(PVSS_ROOT_DIR /opt/pvss/pvss2_v3.7) +set(PVSS_ROOT_DIR /opt/WinCC_OA/3.14) +#set(LOG4CPLUS_ROOT_DIR "/usr/local/log4cplus") +set(CASACORE_ROOT_DIR "/opt/casacore") +set(QPID_ROOT_DIR /opt/qpid) + +#set(CTEST_CUSTOM_WARNING_EXCEPTION +# "/boost/date_time/time_facet.hpp:[0-9]+: warning: unused parameter" +# "/boost/date_time/time.hpp:[0-9]+: warning: unused parameter" +# "/pvss2_v3.7/api/include/(Basics|Datapoint|Manager|Messages)/" +#) diff --git a/CMake/variants/variants.mars b/CMake/variants/variants.mars deleted file mode 100644 index a16ce348913372700c1dcf6e27af1c2df93050ba..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.mars +++ /dev/null @@ -1,2 +0,0 @@ -option(USE_LOG4CPLUS "log4cplus is used" OFF) -option(USE_LOG4CXX "log4cxx is used" OFF) diff --git a/CMake/variants/variants.node501 b/CMake/variants/variants.node501 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.node501 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.node502 b/CMake/variants/variants.node502 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.node502 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.node503 b/CMake/variants/variants.node503 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.node503 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.node521 b/CMake/variants/variants.node521 deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.node521 +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.phi b/CMake/variants/variants.phi deleted file mode 120000 index ce73d06cf965fd9c972ce1ad47120739cdcc0a16..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.phi +++ /dev/null @@ -1 +0,0 @@ -variants.fs5 \ No newline at end of file diff --git a/CMake/variants/variants.sharkbay b/CMake/variants/variants.sharkbay deleted file mode 100644 index 25b6b562669144f890c859117bbd850a56aeb4aa..0000000000000000000000000000000000000000 --- a/CMake/variants/variants.sharkbay +++ /dev/null @@ -1,20 +0,0 @@ -## -*- CMake -*- -## -## Host specific variants file. -## Settings in this file extend or override those in the global variants file. -## -## $Id: variants.fs5 24757 2013-05-01 10:50:49Z loose $ - -option(USE_LOG4CPLUS "Use Log4Cplus" ON) -option(USE_MPI "Use MPI" ON) - -# Refer to symlink that go may through ccache. -# Force GCC 4.7 for CUDA 5.5 -set(GNU_C /usr/lib/ccache/gcc-4.7 ) -set(GNU_CXX /usr/lib/ccache/g++-4.7 ) -set(GNU_Fortran /usr/bin/gfortran ) -set(GNU_ASM /usr/lib/ccache/gcc-4.7 ) - -#set(CUDADRIVER_ROOT_DIR /usr/lib/nvidia-319/) -set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda/) -set(UNITTEST++_ROOT_DIR /usr/include/unittest++) diff --git a/Docker/CMakeLists.txt b/Docker/CMakeLists.txt index 060eea1d0c446ffeb5ac0433df0432c95964c845..a8965c57355c690988016853001b02ad7884e666 100644 --- a/Docker/CMakeLists.txt +++ b/Docker/CMakeLists.txt @@ -25,6 +25,7 @@ lofar_add_bin_program(versiondocker versiondocker.cc) set(DOCKER_TEMPLATE_DIRS lofar-base lofar-pipeline + lofar-pulp lofar-outputproc) # Note: "docker-template" only works as long as the sources are still around, @@ -42,7 +43,7 @@ foreach(_dir ${DOCKER_TEMPLATE_DIRS}) # when "all" is build. add_custom_command( OUTPUT ${_dst} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/docker-template < ${_src} > ${_dst} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/docker-template -v ${CMAKE_CURRENT_BINARY_DIR}/versiondocker < ${_src} > ${_dst} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/docker-template ${_src} ${CMAKE_CURRENT_BINARY_DIR}/versiondocker ) add_custom_target(${_dir}_Dockerfile_target ALL DEPENDS ${_dst}) @@ -57,9 +58,12 @@ endforeach() # Install everything else install(DIRECTORY + dynspec lofar-base lofar-pipeline + lofar-pulp lofar-outputproc + lofar-tbbwriter DESTINATION share/docker USE_SOURCE_PERMISSIONS PATTERN Dockerfile.tmpl EXCLUDE) diff --git a/Docker/docker-build-all.sh b/Docker/docker-build-all.sh index 71eb409a42c9e125646f6f19af6d99e96c176c21..2bcc65546c9ad8a2bc42c4f54a5014c3b344a6b3 100755 --- a/Docker/docker-build-all.sh +++ b/Docker/docker-build-all.sh @@ -11,5 +11,7 @@ cd ${LOFARROOT}/share/docker build lofar-base && \ build lofar-pipeline && \ -build lofar-outputproc +build lofar-outputproc && \ +build lofar-tbb && \ +build dynspec diff --git a/Docker/docker-template b/Docker/docker-template index 61bd6cabfe19422e76a1fa84c9a9cb042c7e4bae..53be24bc26b2e7d7cd7bd500e20a49c978a19df8 100755 --- a/Docker/docker-template +++ b/Docker/docker-template @@ -18,9 +18,34 @@ # ----- LOFAR_BRANCH_NAME = tags/LOFAR-Release-2_15_1 ----- # ----- LOFAR_BRANCH_NAME = UNKNOWN ----- +function usage() { + echo "$0 [-v VERSIONDOCKER]" + echo "" + echo " -v VERSIONDOCKER Provides location of 'versiondocker' executable" + echo "" + exit 1 +} + +# Defaults +VERSION_DOCKER="versiondocker" + +# Parse options +while getopts "hv:" opt; do + case $opt in + h) usage + ;; + v) VERSION_DOCKER="$OPTARG" + ;; + \?) error "Invalid option: -$OPTARG" + ;; + :) error "Option requires an argument: -$OPTARG" + ;; + esac +done + # Make sure we obtain info about the project source! -#PATH=$PATH:. -VERSION_INFO=`(versiondocker || ./versiondocker) 2>/dev/null` # in cmake, executable is in . +# Drop stderr to prevent logger output from contaminating our output +VERSION_INFO=`$VERSION_DOCKER 2>/dev/null` # Extract branch name w.r.t. repository root, e.g. branches/LOFAR-Task1234 export LOFAR_BRANCH_NAME=`echo "$VERSION_INFO" | perl -ne 'print "$1" if /branch += +(.+)/;'` @@ -31,14 +56,14 @@ export LOFAR_BRANCH_NAME=`echo "$VERSION_INFO" | perl -ne 'print "$1" if /branch export LOFAR_BRANCH_URL="https://svn.astron.nl/LOFAR/${LOFAR_BRANCH_NAME}" -# ----- LOFAR_TAG = 1234 ----- +# ----- LOFAR_TAG = LOFAR-Task1234 ----- # ----- LOFAR_TAG = trunk ----- -# ----- LOFAR_TAG = 2_15_1 ----- +# ----- LOFAR_TAG = LOFAR-Release-2_15_1 ----- case "${LOFAR_BRANCH_NAME}" in trunk) export LOFAR_TAG=trunk ;; - tags/*) export LOFAR_TAG=${LOFAR_BRANCH_NAME##tags/LOFAR-Release-} ;; - branches/*) export LOFAR_TAG=${LOFAR_BRANCH_NAME##branches/*Task} ;; + branches/*) export LOFAR_TAG=${LOFAR_BRANCH_NAME##branches/} ;; + tags/*) export LOFAR_TAG=${LOFAR_BRANCH_NAME##tags/} ;; *) export LOFAR_TAG=latest ;; esac diff --git a/Docker/dynspec/Dockerfile b/Docker/dynspec/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1f2c63f57e24cfcade4ff727591e14ab33be6c67 --- /dev/null +++ b/Docker/dynspec/Dockerfile @@ -0,0 +1,147 @@ +# +# base +# +FROM ubuntu:12.04 + +# +# common-environment +# +ENV USER=lofar +ENV INSTALLDIR=/opt + +# +# environment +# +ENV DEBIAN_FRONTEND=noninteractive \ + PYTHON_VERSION=2.7 + +# +# versions +# Requires boost 1.48 +# Remove casacore? +# +ENV CASACORE_VERSION=2.0.3 \ + CASAREST_VERSION=1.4.1 \ + PYTHON_CASACORE_VERSION=2.0.1 \ + BOOST_VERSION=1.48 + +# +# set-uid +# +ENV UID=1000 + +# +# set-build-options +# +ENV J=6 + +# +# Base and runtime dependencies +# +#RUN sed -i 's/archive.ubuntu.com/osmirror.rug.nl/' /etc/apt/sources.list +RUN apt-get update && apt-get upgrade -y +RUN apt-get install -y sudo +#python2.7 libpython2.7 +# apt-get install -y libblas3 liblapacke python-numpy libcfitsio3 libwcs4 libfftw3-bin libhdf5-7 libboost-python${BOOST_VERSION}.0 && \ +# apt-get install -y nano + + + +# +# setup-account +# +RUN echo 'ALL ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + sed -i 's/requiretty/!requiretty/g' /etc/sudoers && \ + useradd -m -u ${UID} ${USER} && \ + chmod a+wr /etc/passwd /etc/group + +# +# setup install dir +# +RUN mkdir -p ${INSTALLDIR} && chown ${USER}.${USER} ${INSTALLDIR} + + +USER ${USER} + +# +# ******************* +# Lofar User Software +# ******************* +# +RUN sudo apt-get update && sudo apt-get upgrade -y && \ + sudo apt-get install -y g++ gfortran flex swig bison subversion \ + zlib1g-dev libatlas-base-dev liblapack-dev \ + libncurses5-dev libfreetype6-dev libpng12-dev \ + python-dev python-tk python-pyfits tk8.5-dev fftw3-dev \ + libbz2-dev libghc-readline-dev \ + git git git-core git-doc git-man git-svn \ + valgrind + +RUN sudo apt-get install -y libboost${BOOST_VERSION}-all-dev \ + wcslib-dev \ + cmake cmake-doc cmake-curses-gui make \ + libgsl0-dev \ + python-matplotlib \ + python-sphinx \ + libcfitsio3-dev \ + python-numpy \ + num-utils \ + python-scipy \ + libblas-dev \ + python-sip-dev \ + openmpi-bin openmpi-common \ + ipython + +RUN cd $INSTALLDIR && svn co http://usg.lofar.org/svn/code/trunk lofarsoft && \ + export LOFARSOFT=${INSTALLDIR}/lofarsoft && . $LOFARSOFT/devel_common/scripts/init.sh && \ + cd $LOFARSOFT && ./bootstrap && cd build && cmake -DCASACORE_FROM_LATEST_SVN_REVISION=ON . && make rebuild_cache && \ + rm -rf $LOFARSOFT/src/Pulsar + +ENV LOFARSOFT=${INSTALLDIR}/lofarsoft + +RUN sudo apt-get install -y libhdf5-serial-dev python-h5py + +RUN . $LOFARSOFT/devel_common/scripts/init.sh && cd $LOFARSOFT/build && make dal + +RUN cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Rebin && \ + g++ -O3 -s -Wall -o DynspecPart *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/Beam2Dynspec-Rebin /usr/local/bin/Beam2Dynspec-Rebin && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Rebin/DynspecPart /usr/local/bin/DynspecPart && \ + cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Quicklook && \ + g++ -O3 -s -Wall -o DynspecQuick *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/Beam2Dynspec-Quick /usr/local/bin/Beam2Dynspec-Quick && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Quicklook/DynspecQuick /usr/local/bin/DynspecQuick && \ + cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Complete && \ + g++ -O3 -s -Wall -o DynspecAll *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/Beam2Dynspec-Complete /usr/local/bin/Beam2Dynspec-Complete && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-CEP2/src/ICD3-ICD6-Complete/DynspecAll /usr/local/bin/DynspecAll && \ + cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Linear-Polar && \ + g++ -O3 -s -Wall -o Dynspec_Linear_Polar *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/Dynspec-LinPol /usr/local/bin/Dynspec-LinPol && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Linear-Polar/Dynspec_Linear_Polar /usr/local/bin/Dynspec_Linear_Polar && \ + cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Rebin && \ + g++ -O3 -s -Wall -o Dyn2Dyn *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/Dynspec-Rebin /usr/local/bin/Dynspec-Rebin && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Rebin/Dyn2Dyn /usr/local/bin/Dyn2Dyn && \ + cd /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Substraction && \ + g++ -O3 -s -Wall -o Dynspec_Substraction *cpp -I /opt/lofarsoft/release//include -L /opt/lofarsoft/release//lib -llofardal -lhdf5 && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/Dynspec-Substract /usr/local/bin/Dynspec-Substract && \ + sudo ln -s /opt/lofarsoft/src/Dynspec-Toolkit/Dynspec-Tool/src/ICD6-Substraction/Dynspec_Substraction /usr/local/bin/Dynspec_Substraction + + + + + +# +# config +# +COPY bashrc /opt/bashrc + +# +# entry +# +#COPY ["bashrc", "bashrc.d", "${INSTALLDIR}/"] + +COPY chuser.sh /usr/local/bin/chuser.sh +ENTRYPOINT ["/usr/local/bin/chuser.sh"] + diff --git a/Docker/dynspec/bashrc b/Docker/dynspec/bashrc new file mode 100644 index 0000000000000000000000000000000000000000..e0755976685f920e1be8d10947b541b996eafef6 --- /dev/null +++ b/Docker/dynspec/bashrc @@ -0,0 +1,15 @@ +#!/bin/bash + +# lofar +[ -r ${INSTALLDIR}/lofar/lofarinit.sh ] && source ${INSTALLDIR}/lofar/lofarinit.sh +export PATH PYTHONPATH LD_LIBRARY_PATH LOFARROOT + +# qpid +#source ${INSTALLDIR}/qpid/.profile + +# lofarsoft +echo "sourcing init" +source ${LOFARSOFT}/devel_common/scripts/init.sh + +export PYTHONPATH=$PYTHONPATH:${LOFARSOFT}/release/lib/python/dal +export LD_LIBRARY_PATH=LD_LIBRARY_PATH:${LOFARSOFT}/release/lib diff --git a/Docker/dynspec/chuser.sh b/Docker/dynspec/chuser.sh new file mode 100755 index 0000000000000000000000000000000000000000..9ac3603c1a072dffff02461e9e8e59b1833b7f4f --- /dev/null +++ b/Docker/dynspec/chuser.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Correct UID +export UID=`id -u` + +# Configure user +if [ -z "${USER}" ]; then + export USER=${UID} +fi + +# Create home directory +if [ -z "${HOME}" ]; then + export HOME=/home/${USER} + mkdir -p $HOME && cd $HOME +fi + +# Add user to system +fgrep -q ":x:${UID}:" /etc/passwd || echo "${USER}:x:${UID}:${UID}::${HOME}:/bin/bash" >> /etc/passwd +fgrep -q ":x:${UID}:" /etc/group || echo "${USER}:x:${UID}:" >> /etc/group + +# Set the environment +[ -e /opt/bashrc ] && source /opt/bashrc + +# Run the requested command +if [ -z "$*" ]; then + exec /bin/bash +else + exec "$@" +fi diff --git a/Docker/lofar-base/Dockerfile.tmpl b/Docker/lofar-base/Dockerfile.tmpl index d15366ba060c2bcc46e8ed726680a6ac2d92fd66..a5679fe25f5246792c469484451e37f3ad1310d3 100644 --- a/Docker/lofar-base/Dockerfile.tmpl +++ b/Docker/lofar-base/Dockerfile.tmpl @@ -1,12 +1,11 @@ # # base # -FROM ubuntu:14.04 +FROM ubuntu:16.04 # # common-environment # -ENV USER=lofar ENV INSTALLDIR=/opt # @@ -18,89 +17,84 @@ ENV DEBIAN_FRONTEND=noninteractive \ # # versions # -ENV CASACORE_VERSION=2.0.3 \ - CASAREST_VERSION=1.4.1 \ - PYTHON_CASACORE_VERSION=2.0.1 \ - BOOST_VERSION=1.54 - -# -# set-uid -# -ENV UID=${BUILD_UID} +ENV CASACORE_VERSION=latest \ + CASAREST_VERSION=latest \ + PYTHON_CASACORE_VERSION=2.1.2 \ + BOOST_VERSION=1.58 # # set-build-options # -ENV J=4 +ENV J=6 # # Base and runtime dependencies # #RUN sed -i 's/archive.ubuntu.com/osmirror.rug.nl/' /etc/apt/sources.list -RUN apt-get update && apt-get upgrade -y -RUN apt-get install -y sudo python2.7 libpython2.7 && \ - apt-get install -y libblas3 liblapacke python-numpy libcfitsio3 libwcs4 libfftw3-bin libhdf5-7 libboost-python${BOOST_VERSION}.0 +RUN apt-get update && \ + apt-get install -y python2.7 libpython2.7 && \ + apt-get install -y libopenblas-base libcfitsio-bin libwcs5 libfftw3-bin libhdf5-10 libboost-python${BOOST_VERSION}.0 && \ + apt-get install -y python-pip && \ + pip install numpy && \ + apt-get purge -y python-pip && \ + apt-get autoremove -y --purge && \ + apt-get install -y nano sudo # -# setup-account +# open security holes (allow smooth user switching, allow sudo) # -RUN (getent group sudo &>/dev/null || groupadd sudo) && \ - echo "useradd -m ${USERADD_FLAGS} ${USER}" && \ - useradd -m -u ${UID} ${USER} && \ - usermod -a -G sudo ${USER} && \ - echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ - sed -i 's/requiretty/!requiretty/g' /etc/sudoers +RUN echo 'ALL ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + sed -i 's/requiretty/!requiretty/g' /etc/sudoers && \ + chmod a+rw /etc/group /etc/passwd # # setup install dir # -RUN mkdir -p ${INSTALLDIR} && chown ${USER}.${USER} ${INSTALLDIR} - -USER ${USER} +RUN mkdir -p ${INSTALLDIR} # # ******************* # Casacore # ******************* # -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y wget git cmake g++ gfortran flex bison libblas-dev liblapacke-dev libfftw3-dev libhdf5-dev libboost-python-dev libcfitsio3-dev wcslib-dev && \ +RUN apt-get update && apt-get install -y wget git cmake g++ gfortran flex bison libopenblas-dev libfftw3-dev libhdf5-dev libboost-python-dev libcfitsio-dev wcslib-dev && \ mkdir -p ${INSTALLDIR}/casacore/build && \ mkdir -p ${INSTALLDIR}/casacore/data && \ cd ${INSTALLDIR}/casacore && git clone https://github.com/casacore/casacore.git src && \ if [ "${CASACORE_VERSION}" != "latest" ]; then cd ${INSTALLDIR}/casacore/src && git checkout tags/v${CASACORE_VERSION}; fi && \ cd ${INSTALLDIR}/casacore/data && wget --retry-connrefused ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar && \ cd ${INSTALLDIR}/casacore/data && tar xf WSRT_Measures.ztar && rm -f WSRT_Measures.ztar && \ - cd ${INSTALLDIR}/casacore/build && cmake -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/casacore/ -DDATA_DIR=${INSTALLDIR}/casacore/data -DBUILD_PYTHON=True -DUSE_OPENMP=True -DUSE_FFTW3=TRUE ../src/ && \ + cd ${INSTALLDIR}/casacore/build && cmake -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/casacore/ -DDATA_DIR=${INSTALLDIR}/casacore/data -DBUILD_PYTHON=True -DENABLE_TABLELOCKING=ON -DUSE_OPENMP=ON -DUSE_FFTW3=TRUE -DUSE_HDF5=ON -DCXX11=YES -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fsigned-char -O2 -DNDEBUG -march=native" ../src/ && \ cd ${INSTALLDIR}/casacore/build && make -j ${J} && \ cd ${INSTALLDIR}/casacore/build && make install && \ bash -c "strip ${INSTALLDIR}/casacore/{lib,bin}/* || true" && \ bash -c "rm -rf ${INSTALLDIR}/casacore/{build,src}" && \ - sudo apt-get purge -y wget git cmake g++ gfortran flex bison libblas-dev liblapacke-dev libfftw3-dev libhdf5-dev libboost-python-dev libcfitsio3-dev wcslib-dev && \ - sudo apt-get autoremove -y + apt-get purge -y wget git cmake g++ gfortran flex bison libopenblas-dev libfftw3-dev libhdf5-dev libboost-python-dev libcfitsio-dev wcslib-dev && \ + apt-get autoremove -y --purge # # ******************* # Casarest # ******************* # -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y git cmake g++ gfortran libboost-system-dev libboost-thread-dev libhdf5-dev libcfitsio3-dev wcslib-dev && \ +RUN apt-get update && apt-get install -y git cmake g++ gfortran libboost-system-dev libboost-thread-dev libhdf5-dev libcfitsio3-dev wcslib-dev libopenblas-dev && \ mkdir -p ${INSTALLDIR}/casarest/build && \ cd ${INSTALLDIR}/casarest && git clone https://github.com/casacore/casarest.git src && \ if [ "${CASAREST_VERSION}" != "latest" ]; then cd ${INSTALLDIR}/casarest/src && git checkout tags/v${CASAREST_VERSION}; fi && \ - cd ${INSTALLDIR}/casarest/build && cmake -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/casarest -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore ../src/ && \ + cd ${INSTALLDIR}/casarest/build && cmake -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/casarest -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-std=c++11 -O2 -march=native -DNDEBUG" ../src/ && \ cd ${INSTALLDIR}/casarest/build && make -j ${J} && \ cd ${INSTALLDIR}/casarest/build && make install && \ bash -c "strip ${INSTALLDIR}/casarest/{lib,bin}/* || true" && \ bash -c "rm -rf ${INSTALLDIR}/casarest/{build,src}" && \ - sudo apt-get purge -y git cmake g++ gfortran libboost-system-dev libboost-thread-dev libhdf5-dev libcfitsio3-dev wcslib-dev && \ - sudo apt-get autoremove -y + apt-get purge -y git cmake g++ gfortran libboost-system-dev libboost-thread-dev libhdf5-dev libcfitsio3-dev wcslib-dev libopenblas-dev && \ + apt-get autoremove -y --purge # # ******************* # Pyrap # ******************* # -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y git make g++ python-setuptools libboost-python-dev libcfitsio3-dev wcslib-dev && \ +RUN apt-get update && apt-get install -y git make g++ python-setuptools libboost-python-dev libcfitsio3-dev wcslib-dev && \ mkdir ${INSTALLDIR}/python-casacore && \ cd ${INSTALLDIR}/python-casacore && git clone https://github.com/casacore/python-casacore && \ if [ "$PYTHON_CASACORE_VERSION" != "latest" ]; then cd ${INSTALLDIR}/python-casacore/python-casacore && git checkout tags/v${PYTHON_CASACORE_VERSION}; fi && \ @@ -110,8 +104,8 @@ RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y gi export PYTHONPATH=${INSTALLDIR}/python-casacore/lib/python${PYTHON_VERSION}/site-packages:${INSTALLDIR}/python-casacore/lib64/python${PYTHON_VERSION}/site-packages:$PYTHONPATH && cd ${INSTALLDIR}/python-casacore/python-casacore && ./setup.py install --prefix=${INSTALLDIR}/python-casacore/ && \ bash -c "find ${INSTALLDIR}/python-casacore/lib -name '*.so' | xargs strip || true" && \ bash -c "rm -rf ${INSTALLDIR}/python-casacore/python-casacore" && \ - sudo apt-get purge -y git make g++ python-setuptools libboost-python-dev libcfitsio3-dev wcslib-dev && \ - sudo apt-get autoremove -y + apt-get purge -y git make g++ python-setuptools libboost-python-dev libcfitsio3-dev wcslib-dev && \ + apt-get autoremove -y --purge # # ******************* @@ -119,37 +113,26 @@ RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y gi # ******************* # -# Allow auto-checkout from lofar repo. We create ~/.subversion/servers in-line to prevent Docker cache invalidation -RUN mkdir /home/${USER}/.subversion -RUN echo '\ -[groups] \n\ -astron = svn.astron.nl \n\ -\n\ -[astron] \n\ -store-passwords = yes \n\ -store-plaintext-passwords = yes \n\ -' > /home/${USER}/.subversion/servers - # Run-time dependencies # QPID daemon legacy store would require: libaio1 libdb5.1++ -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y sasl2-bin libuuid1 libnss3 libnspr4 xqilla libboost-program-options${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 +RUN apt-get update && apt-get install -y sasl2-bin libuuid1 libnss3 libnspr4 xqilla libboost-program-options${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 # Install # QPID daemon legacy store would require: libaio-dev libdb5.1++-dev -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y subversion swig ruby ruby-dev python-dev libsasl2-dev pkg-config cmake libtool uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man fakeroot build-essential debhelper libsslcommon2-dev libxqilla-dev python-setuptools libboost-program-options${BOOST_VERSION}-dev libboost-filesystem${BOOST_VERSION}-dev && \ +RUN apt-get update && apt-get install -y subversion swig ruby ruby-dev python-dev libsasl2-dev pkg-config cmake libtool uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man fakeroot build-essential debhelper libsslcommon2-dev libxqilla-dev python-setuptools libboost-program-options${BOOST_VERSION}-dev libboost-filesystem${BOOST_VERSION}-dev && \ mkdir /opt/qpid && \ - svn --non-interactive -q --username lofar-guest --password lofar-guest co ${LOFAR_BRANCH_URL}/LCS/MessageBus/qpid/ /opt/qpid; \ + svn --non-interactive -q co ${LOFAR_BRANCH_URL}/LCS/MessageBus/qpid/ /opt/qpid; \ /opt/qpid/local/sbin/build_qpid && \ bash -c "strip /opt/qpid/{bin,lib}/* || true" && \ bash -c "rm -rf ~/sources" && \ - sudo apt-get purge -y subversion swig ruby ruby-dev python-dev libsasl2-dev pkg-config cmake libtool uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man fakeroot build-essential debhelper libsslcommon2-dev libxqilla-dev python-setuptools libboost-program-options${BOOST_VERSION}-dev libboost-filesystem${BOOST_VERSION}-dev && \ - sudo apt-get autoremove -y + apt-get purge -y subversion swig ruby ruby-dev python-dev libsasl2-dev pkg-config cmake libtool uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man fakeroot build-essential debhelper libsslcommon2-dev libxqilla-dev python-setuptools libboost-program-options${BOOST_VERSION}-dev libboost-filesystem${BOOST_VERSION}-dev && \ + apt-get autoremove -y --purge # # ******************* # DAL # ******************* # -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y git cmake g++ swig python-dev libhdf5-dev && \ +RUN apt-get update && apt-get install -y git cmake g++ swig python-dev libhdf5-dev && \ mkdir ${INSTALLDIR}/DAL && \ cd ${INSTALLDIR}/DAL && git clone https://github.com/nextgen-astrodata/DAL.git src && \ mkdir ${INSTALLDIR}/DAL/build && cd ${INSTALLDIR}/DAL/build && cmake -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/DAL ../src && \ @@ -157,18 +140,14 @@ RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y gi make install && \ bash -c "find ${INSTALLDIR}/DAL/lib -name '*.so' | xargs strip || true" && \ bash -c "rm -rf ${INSTALLDIR}/DAL/{src,build}" && \ - sudo apt-get purge -y git cmake g++ swig python-dev libhdf5-dev && \ - sudo apt-get autoremove -y - -# -# config -# -COPY bashrc /home/${USER}/.bashrc + apt-get purge -y git cmake g++ swig python-dev libhdf5-dev && \ + apt-get autoremove -y --purge # # entry # -COPY chuser.sh /usr/local/bin/chuser.sh -WORKDIR /home/${USER} -ENTRYPOINT ["sudo","-E","/usr/local/bin/chuser.sh"] +COPY ["bashrc", "${INSTALLDIR}/"] +COPY ["bashrc.d", "${INSTALLDIR}/bashrc.d/"] +COPY ["chuser.sh", "/usr/local/bin"] +ENTRYPOINT ["/usr/local/bin/chuser.sh"] diff --git a/Docker/lofar-base/bashrc b/Docker/lofar-base/bashrc index 74dbbeef1254fd1c50e3454a8b19b441379788d1..b73fc3d6d3d7d48b0348c0a46b179a351eb94f36 100644 --- a/Docker/lofar-base/bashrc +++ b/Docker/lofar-base/bashrc @@ -1,13 +1,7 @@ #!/bin/bash -# lofar -[ -r ${INSTALLDIR}/lofar/lofarinit.sh ] && source ${INSTALLDIR}/lofar/lofarinit.sh - -# qpid -source ${INSTALLDIR}/qpid/.profile - -# python-casacore -export PYTHONPATH=${PYTHONPATH}:${INSTALLDIR}/python-casacore/lib/python2.7/site-packages - -# casacore -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${INSTALLDIR}/casacore/lib +# Read all profiles +shopt -s nullglob +for rc in ${INSTALLDIR}/bashrc.d/*; do + source $rc +done diff --git a/Docker/lofar-base/bashrc.d/00-casacore b/Docker/lofar-base/bashrc.d/00-casacore new file mode 100644 index 0000000000000000000000000000000000000000..dbda097bf8a50f27b42246b9a90f182c1f09a292 --- /dev/null +++ b/Docker/lofar-base/bashrc.d/00-casacore @@ -0,0 +1,3 @@ +#!/bin/bash +export PATH=${PATH}:${INSTALLDIR}/casacore/bin +export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${INSTALLDIR}/casacore/lib diff --git a/Docker/lofar-base/bashrc.d/00-qpid b/Docker/lofar-base/bashrc.d/00-qpid new file mode 100644 index 0000000000000000000000000000000000000000..bf491f5fbcfbb0ada9be7a310365c544960d72e2 --- /dev/null +++ b/Docker/lofar-base/bashrc.d/00-qpid @@ -0,0 +1,2 @@ +#!/bin/bash +source ${INSTALLDIR}/qpid/.profile diff --git a/Docker/lofar-base/bashrc.d/01-python-casacore b/Docker/lofar-base/bashrc.d/01-python-casacore new file mode 100644 index 0000000000000000000000000000000000000000..d58385ad5180bb26c9baeaf678e73bcfd82734d1 --- /dev/null +++ b/Docker/lofar-base/bashrc.d/01-python-casacore @@ -0,0 +1,2 @@ +#!/bin/bash +export PYTHONPATH=${PYTHONPATH}:${INSTALLDIR}/python-casacore/lib/python2.7/site-packages diff --git a/Docker/lofar-base/bashrc.d/50-lofar b/Docker/lofar-base/bashrc.d/50-lofar new file mode 100644 index 0000000000000000000000000000000000000000..10ba02c20fdec38dd112dfcc5eb2ec5495a9f8d2 --- /dev/null +++ b/Docker/lofar-base/bashrc.d/50-lofar @@ -0,0 +1,3 @@ +#!/bin/bash +[ -r ${INSTALLDIR}/lofar/lofarinit.sh ] && source ${INSTALLDIR}/lofar/lofarinit.sh +export PATH PYTHONPATH LD_LIBRARY_PATH LOFARROOT diff --git a/Docker/lofar-base/chuser.sh b/Docker/lofar-base/chuser.sh index 294e58c7d30e9288aa263d632c738a8ab06c0b98..46872545a87da2e2516bc3ab2e443644c8ee6281 100755 --- a/Docker/lofar-base/chuser.sh +++ b/Docker/lofar-base/chuser.sh @@ -1,24 +1,26 @@ #!/usr/bin/env bash -# Fetch the user name used in this container -export USER=${SUDO_USER} +# Configure user +if [ -z "${USER}" ]; then + export USER=${UID} +fi -if [ -n "${LUSER}" ]; then - if [ -z "${LGROUP}" ]; then - LGROUP=${LUSER} - fi +# Create home directory +if [ -z "${HOME}" ]; then + export HOME=/home/${USER} + mkdir -p $HOME && cd $HOME +fi - OLDID=`id -u ${USER}` +# Add user to system +fgrep -q ":x:${UID}:" /etc/passwd || echo "${USER}:x:${UID}:${UID}::${HOME}:/bin/bash" >> /etc/passwd +fgrep -q ":x:${UID}:" /etc/group || echo "${USER}:x:${UID}:" >> /etc/group - # Replace USER by LUSER:LGROUP - sed -i -e "s/${USER}:x:[0-9]\+:[0-9]\+/${USER}:x:${LUSER}:${LGROUP}/g" /etc/passwd - sed -i -e "s/${USER}:x:[0-9]\+:/${USER}:x:${LGROUP}:/g" /etc/group +# Set the environment +[ -e /opt/bashrc ] && source /opt/bashrc - # Set ownership of home dir to new user - chown --from=${OLDID} -R ${LUSER}:${LGROUP} ${HOME} +# Run the requested command +if [ -z "$*" ]; then + exec /bin/bash +else + exec "$@" fi - -# Switch to the updated user -export HOME=/home/${USER} -touch -a $HOME/.bashrc -sudo -u ${USER} -E -s /bin/bash -c "source $HOME/.bashrc;$*" diff --git a/Docker/lofar-outputproc/Dockerfile.tmpl b/Docker/lofar-outputproc/Dockerfile.tmpl index d0b55ec21d39100b739be9ee1a2a226f5e0365f6..818e940c86158454f0585711c5af4a457b16f3c0 100644 --- a/Docker/lofar-outputproc/Dockerfile.tmpl +++ b/Docker/lofar-outputproc/Dockerfile.tmpl @@ -10,25 +10,28 @@ FROM lofar-base:${LOFAR_TAG} # # Run-time dependencies -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y liblog4cplus-1.0-4 libxml2 libboost-thread${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 libboost-date-time${BOOST_VERSION}.0 libpng12-0 libsigc++-2.0-dev libxml++2.6-2 libboost-regex${BOOST_VERSION}.0 +RUN apt-get update && apt-get install -y binutils liblog4cplus-1.1-9 libxml2 libboost-thread${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 libboost-date-time${BOOST_VERSION}.0 libpng12-0 libsigc++-2.0-dev libxml++2.6-2v5 libboost-regex${BOOST_VERSION}.0 # Tell image build information -ENV LOFAR_BRANCH=${LOFAR_BRANCH_NAME} \ - LOFAR_REVISION=${LOFAR_REVISION} +ENV LOFAR_BRANCH=${LOFAR_BRANCH} \ + LOFAR_REVISION=${LOFAR_REVISION} \ + LOFAR_BUILDVARIANT=gnucxx11_optarch # Install -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y subversion cmake g++ gfortran bison flex autogen liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python${BOOST_VERSION}-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libboost-regex${BOOST_VERSION} binutils-dev && \ - mkdir -p ${INSTALLDIR}/lofar/build/gnu_opt libcfitsio3-dev wcslib-dev && \ +RUN apt-get update && apt-get install -y subversion cmake g++ gfortran bison flex autogen liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python${BOOST_VERSION}-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libboost-regex${BOOST_VERSION} binutils-dev libopenblas-dev libcfitsio3-dev wcslib-dev && \ + mkdir -p ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && \ cd ${INSTALLDIR}/lofar && \ - svn --non-interactive -q --username lofar-guest --password lofar-guest co -r ${LOFAR_REVISION} -N ${LOFAR_BRANCH_URL} src; \ + svn --non-interactive -q co -r ${LOFAR_REVISION} -N ${LOFAR_BRANCH_URL} src; \ svn --non-interactive -q up src/CMake && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && cmake -DBUILD_PACKAGES=Online_OutputProc -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/lofar/ -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DLOG4CPLUS_ROOT_DIR=${INSTALLDIR}/log4cplus/ -DQPID_ROOT_DIR=/opt/qpid/ -DDAL_ROOT_DIR=${INSTALLDIR}/DAL -DUSE_OPENMP=True ${INSTALLDIR}/lofar/src/ && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && sed -i '29,31d' include/ApplCommon/PosixTime.h && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && make -j ${J} && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && make install && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && cmake -DBUILD_PACKAGES=Online_OutputProc -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/lofar/ -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DQPID_ROOT_DIR=/opt/qpid/ -DDAL_ROOT_DIR=${INSTALLDIR}/DAL -DUSE_OPENMP=True ${INSTALLDIR}/lofar/src/ && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && sed -i '29,31d' include/ApplCommon/PosixTime.h && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make -j ${J} && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make install && \ + bash -c "mkdir -p /home/${USER}/lofar/var/{log,run}" && \ + bash -c "ln -sfT /home/${USER}/lofar/var ${INSTALLDIR}/lofar/var" && \ bash -c "strip ${INSTALLDIR}/lofar/{bin,sbin,lib64}/* || true" && \ bash -c "rm -rf ${INSTALLDIR}/lofar/{build,src}" && \ - sudo setcap cap_sys_nice,cap_ipc_lock=ep ${INSTALLDIR}/lofar/bin/outputProc && \ - sudo apt-get purge -y subversion cmake g++ gfortran bison flex autogen liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python${BOOST_VERSION}-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev binutils-dev libcfitsio3-dev wcslib-dev && \ - sudo apt-get autoremove -y + setcap cap_sys_nice,cap_sys_admin=ep ${INSTALLDIR}/lofar/bin/outputProc && \ + apt-get purge -y subversion cmake g++ gfortran bison flex autogen liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python${BOOST_VERSION}-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev binutils-dev libcfitsio3-dev wcslib-dev libopenblas-dev && \ + apt-get autoremove -y --purge diff --git a/Docker/lofar-pipeline/Dockerfile.tmpl b/Docker/lofar-pipeline/Dockerfile.tmpl index 7bffe2469271b442cee941384ec818630f1292f2..cd2c9a16f53de6e2872834b81e5595624d670399 100644 --- a/Docker/lofar-pipeline/Dockerfile.tmpl +++ b/Docker/lofar-pipeline/Dockerfile.tmpl @@ -3,14 +3,14 @@ # FROM lofar-base:${LOFAR_TAG} -ENV AOFLAGGER_VERSION=2.7.1 +ENV AOFLAGGER_VERSION=2.8.0 # Run-time dependencies -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y python-xmlrunner python-scipy liblog4cplus-1.0-4 libxml2 libboost-thread${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 libboost-date-time${BOOST_VERSION}.0 libboost-signals${BOOST_VERSION}.0 libpng12-0 libsigc++-2.0-dev libxml++2.6-2 libgsl0ldbl openssh-client libboost-regex${BOOST_VERSION}.0 && \ - sudo apt-get -y install python-pip python-dev && \ - sudo pip install pyfits pywcs python-monetdb && \ - sudo apt-get -y purge python-pip python-dev && \ - sudo apt-get -y autoremove +RUN apt-get update && apt-get install -y python-xmlrunner python-scipy liblog4cplus-1.1-9 libxml2 libboost-thread${BOOST_VERSION}.0 libboost-filesystem${BOOST_VERSION}.0 libboost-date-time${BOOST_VERSION}.0 libboost-signals${BOOST_VERSION}.0 libpng12-0 libsigc++-2.0-dev libxml++2.6-2v5 libgsl2 openssh-client libboost-regex${BOOST_VERSION}.0 gettext-base rsync python-matplotlib ipython slurm-client && \ + apt-get -y install python-pip python-dev && \ + pip install pyfits pywcs python-monetdb && \ + apt-get -y purge python-pip python-dev && \ + apt-get -y autoremove --purge # # ******************* @@ -18,18 +18,18 @@ RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y py # ******************* # -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y wget cmake g++ libxml++2.6-dev libpng12-dev libfftw3-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-signals${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libcfitsio3-dev && \ +RUN apt-get update && apt-get install -y wget cmake g++ libxml++2.6-dev libpng12-dev libfftw3-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-signals${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libcfitsio3-dev libopenblas-dev && \ mkdir -p ${INSTALLDIR}/aoflagger/build && \ bash -c "cd ${INSTALLDIR}/aoflagger && wget --retry-connrefused http://downloads.sourceforge.net/project/aoflagger/aoflagger-${AOFLAGGER_VERSION%%.?}.0/aoflagger-${AOFLAGGER_VERSION}.tar.bz2" && \ cd ${INSTALLDIR}/aoflagger && tar xf aoflagger-${AOFLAGGER_VERSION}.tar.bz2 && \ - cd ${INSTALLDIR}/aoflagger/build && cmake -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/aoflagger ../aoflagger-${AOFLAGGER_VERSION} && \ + cd ${INSTALLDIR}/aoflagger/build && cmake -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_FLAGS="--std=c++11 -O2 -DNDEBUG" -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/aoflagger ../aoflagger-${AOFLAGGER_VERSION} && \ cd ${INSTALLDIR}/aoflagger/build && make -j ${J} && \ cd ${INSTALLDIR}/aoflagger/build && make install && \ bash -c "strip ${INSTALLDIR}/aoflagger/{lib,bin}/* || true" && \ bash -c "rm -rf ${INSTALLDIR}/aoflagger/{build,aoflagger-${AOFLAGGER_VERSION}}" && \ bash -c "rm -rf ${INSTALLDIR}/aoflagger/aoflagger-${AOFLAGGER_VERSION}.tar.bz2" && \ - sudo apt-get -y purge wget cmake g++ libxml++2.6-dev libpng12-dev libfftw3-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-signals${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libcfitsio3-dev && \ - sudo apt-get -y autoremove + apt-get -y purge wget cmake g++ libxml++2.6-dev libpng12-dev libfftw3-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-signals${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libcfitsio3-dev libopenblas-dev && \ + apt-get -y autoremove --purge # # ******************* @@ -39,20 +39,23 @@ RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y wg # Tell image build information ENV LOFAR_BRANCH=${LOFAR_BRANCH_NAME} \ - LOFAR_REVISION=${LOFAR_REVISION} + LOFAR_REVISION=${LOFAR_REVISION} \ + LOFAR_BUILDVARIANT=gnucxx11_optarch + # Install -RUN sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y subversion cmake g++ gfortran bison flex liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python-dev python-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libgsl0-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libboost-regex${BOOST_VERSION} binutils-dev libcfitsio3-dev wcslib-dev && \ - mkdir -p ${INSTALLDIR}/lofar/build/gnu_opt && \ +RUN apt-get update && apt-get install -y subversion cmake g++ gfortran bison flex liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python-dev python-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libgsl-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev libboost-regex${BOOST_VERSION} binutils-dev libcfitsio3-dev wcslib-dev libopenblas-dev && \ + mkdir -p ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && \ cd ${INSTALLDIR}/lofar && \ - svn --non-interactive -q --username lofar-guest --password lofar-guest co -r ${LOFAR_REVISION} -N ${LOFAR_BRANCH_URL} src; \ + svn --non-interactive -q co -r ${LOFAR_REVISION} -N ${LOFAR_BRANCH_URL} src; \ svn --non-interactive -q up src/CMake && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && cmake -DBUILD_PACKAGES=Offline -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/lofar/ -DCASAREST_ROOT_DIR=${INSTALLDIR}/casarest/ -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DAOFLAGGER_ROOT_DIR=${INSTALLDIR}/aoflagger/ -DLOG4CPLUS_ROOT_DIR=${INSTALLDIR}/log4cplus/ -DQPID_ROOT_DIR=/opt/qpid/ -DUSE_OPENMP=True ${INSTALLDIR}/lofar/src/ && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && sed -i '29,31d' include/ApplCommon/PosixTime.h && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && make -j ${J} && \ - cd ${INSTALLDIR}/lofar/build/gnu_opt && make install && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && cmake -DBUILD_PACKAGES=Offline -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/lofar/ -DCASAREST_ROOT_DIR=${INSTALLDIR}/casarest/ -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DAOFLAGGER_ROOT_DIR=${INSTALLDIR}/aoflagger/ -DQPID_ROOT_DIR=/opt/qpid/ -DUSE_OPENMP=True ${INSTALLDIR}/lofar/src/ && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && sed -i '29,31d' include/ApplCommon/PosixTime.h && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make -j ${J} && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make install && \ + bash -c "mkdir -p ${INSTALLDIR}/lofar/var/{log,run}" && \ + bash -c "chmod a+rwx ${INSTALLDIR}/lofar/var/{log,run}" && \ bash -c "strip ${INSTALLDIR}/lofar/{bin,sbin,lib64}/* || true" && \ bash -c "rm -rf ${INSTALLDIR}/lofar/{build,src}" && \ - sudo apt-get purge -y subversion cmake g++ gfortran bison flex liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python-dev python-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libgsl0-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev binutils-dev wcslib-dev && \ - sudo apt-get autoremove -y - + apt-get purge -y subversion cmake g++ gfortran bison flex liblog4cplus-dev libhdf5-dev libblitz0-dev libboost-dev libboost-python-dev python-dev libxml2-dev pkg-config libpng12-dev libfftw3-dev libunittest++-dev libxml++2.6-dev libgsl-dev libboost-filesystem${BOOST_VERSION}-dev libboost-date-time${BOOST_VERSION}-dev libboost-thread${BOOST_VERSION}-dev binutils-dev libcfitsio3-dev wcslib-dev libopenblas-dev && \ + apt-get autoremove -y --purge diff --git a/Docker/lofar-pulp/Dockerfile.tmpl b/Docker/lofar-pulp/Dockerfile.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..f214fa8d7659577aafe7d7fd87f2f78847624d16 --- /dev/null +++ b/Docker/lofar-pulp/Dockerfile.tmpl @@ -0,0 +1,62 @@ +# +# base +# +FROM pulp:latest + +COPY ["sudoers", "/etc/"] + +# Run-time dependencies +RUN sudo apt-get update && sudo apt-get install -y python-xmlrunner liblog4cplus-1.0-4 libxml2 libxml++2.6-2 openssh-client gettext-base rsync python-matplotlib +# +# ******************* +# QPID client +# ******************* +# + +ENV BOOST_VERSION=1.54 + +# Run-time dependencies +# QPID daemon legacy store would require: libaio1 libdb5.1++ +RUN sudo apt-get update && sudo apt-get install -y sasl2-bin libuuid1 libnss3 libnspr4 xqilla + +# Install +# QPID daemon legacy store would require: libaio-dev libdb5.1++-dev +RUN sudo apt-get update && sudo apt-get install -y ruby ruby-dev libsasl2-dev uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man libsslcommon2-dev libxqilla-dev && \ + mkdir /opt/qpid && \ + svn --non-interactive -q co https://svn.astron.nl/LOFAR/trunk/LCS/MessageBus/qpid/ /opt/qpid; \ + bash -c "HOME=/tmp /opt/qpid/local/sbin/build_qpid" && \ + bash -c "strip /opt/qpid/{bin,lib}/* || true" && \ + bash -c "rm -rf /tmp/sources" && \ + sudo apt-get purge -y ruby ruby-dev libsasl2-dev uuid-dev libxerces-c-dev libnss3-dev libnspr4-dev help2man libsslcommon2-dev libxqilla-dev && \ + sudo apt-get autoremove -y + +# +# ******************* +# LOFAR +# ******************* +# + +# Tell image build information +ENV LOFAR_BRANCH=${LOFAR_BRANCH_NAME} \ + LOFAR_TAG=${LOFAR_TAG} \ + LOFAR_REVISION=${LOFAR_REVISION} \ + LOFAR_BUILDVARIANT=gnu_optarch + +# Install +#some are already installed, but we need boost, and RUN sudo apt-get update && sudo apt-get install -y subversion cmake g++ gfortran bison flex liblog4cplus-dev libhdf5-dev libblitz0-dev python-dev libxml2-dev pkg-config libunittest++-dev libxml++2.6-dev binutils-dev && \ +RUN sudo apt-get update && sudo apt-get install -y liblog4cplus-dev libhdf5-dev libblitz0-dev libunittest++-dev libxml++2.6-dev binutils-dev && \ + mkdir -p ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && \ + cd ${INSTALLDIR}/lofar && \ + svn --non-interactive -q co -r ${LOFAR_REVISION} -N ${LOFAR_BRANCH_URL} src; \ + svn --non-interactive -q up src/CMake && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && cmake -DBUILD_PACKAGES="Pipeline MessageBus OTDB_Services" -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/lofar/ -DCASACORE_ROOT_DIR=${INSTALLDIR}/casacore/ -DQPID_ROOT_DIR=/opt/qpid/ -DUSE_OPENMP=True ${INSTALLDIR}/lofar/src/ && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make -j ${J} && \ + cd ${INSTALLDIR}/lofar/build/${LOFAR_BUILDVARIANT} && make install && \ + bash -c "mkdir -p ${INSTALLDIR}/lofar/var/{log,run}" && \ + bash -c "chmod a+rwx ${INSTALLDIR}/lofar/var/{log,run}" && \ + bash -c "strip ${INSTALLDIR}/lofar/{bin,sbin,lib64}/* || true" && \ + bash -c "rm -rf ${INSTALLDIR}/lofar/{build,src}" && \ + sudo apt-get purge -y liblog4cplus-dev libhdf5-dev libblitz0-dev libunittest++-dev libxml++2.6-dev binutils-dev && \ + sudo apt-get autoremove -y + +COPY ["bashrc", "/opt/"] diff --git a/Docker/lofar-pulp/bashrc b/Docker/lofar-pulp/bashrc new file mode 100644 index 0000000000000000000000000000000000000000..7c9e948ce7e52aefa446f3db4a7f8df05afc0d4b --- /dev/null +++ b/Docker/lofar-pulp/bashrc @@ -0,0 +1,11 @@ +#!/bin/bash + +# lofar +[ -r ${INSTALLDIR}/lofar/lofarinit.sh ] && source ${INSTALLDIR}/lofar/lofarinit.sh +export PATH PYTHONPATH LD_LIBRARY_PATH LOFARROOT + +# qpid +source ${INSTALLDIR}/qpid/.profile + +# lofarsoft +source ${LOFARSOFT}/devel_common/scripts/init.sh diff --git a/Docker/lofar-pulp/sudoers b/Docker/lofar-pulp/sudoers new file mode 100644 index 0000000000000000000000000000000000000000..24c18523f4bf099e24b3c37970c974deeb796927 --- /dev/null +++ b/Docker/lofar-pulp/sudoers @@ -0,0 +1 @@ +ALL ALL=(ALL) NOPASSWD:ALL diff --git a/Docker/lofar-tbbwriter/Dockerfile b/Docker/lofar-tbbwriter/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..955064aa755af440788ea15bd2e99b103f44cc0d --- /dev/null +++ b/Docker/lofar-tbbwriter/Dockerfile @@ -0,0 +1,118 @@ +# +# base +# +FROM ubuntu:12.04 + +# +# common-environment +# +ENV USER=lofar +ENV INSTALLDIR=/opt + +# +# environment +# +ENV DEBIAN_FRONTEND=noninteractive \ + PYTHON_VERSION=2.7 + +# +# versions +# Requires boost 1.48 +# Remove casacore? +# +ENV CASACORE_VERSION=2.0.3 \ + CASAREST_VERSION=1.4.1 \ + PYTHON_CASACORE_VERSION=2.0.1 \ + BOOST_VERSION=1.48 + +# +# set-uid +# +ENV UID=1000 + +# +# set-build-options +# +ENV J=6 + +# +# Base and runtime dependencies +# +#RUN sed -i 's/archive.ubuntu.com/osmirror.rug.nl/' /etc/apt/sources.list +RUN apt-get update && apt-get upgrade -y +RUN apt-get install -y sudo +#python2.7 libpython2.7 +# apt-get install -y libblas3 liblapacke python-numpy libcfitsio3 libwcs4 libfftw3-bin libhdf5-7 libboost-python${BOOST_VERSION}.0 && \ +# apt-get install -y nano + +# +# setup-account +# +RUN (getent group sudo &>/dev/null || groupadd sudo) && \ + echo "useradd -m ${USERADD_FLAGS} ${USER}" && \ + useradd -m -u ${UID} ${USER} && \ + usermod -a -G sudo ${USER} && \ + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + sed -i 's/requiretty/!requiretty/g' /etc/sudoers + +# +# setup install dir +# +RUN mkdir -p ${INSTALLDIR} && chown ${USER}.${USER} ${INSTALLDIR} + +USER ${USER} + + +# +# ******************* +# Lofar User Software +# ******************* +# +RUN sudo apt-get update && sudo apt-get upgrade -y && \ + sudo apt-get install -y g++ gfortran flex swig bison subversion \ + zlib1g-dev libatlas-base-dev liblapack-dev \ + libncurses5-dev libfreetype6-dev libpng12-dev \ + python-dev python-tk python-pyfits tk8.5-dev fftw3-dev \ + libbz2-dev libghc-readline-dev \ + git git git-core git-doc git-man git-svn \ + valgrind + +RUN sudo apt-get install -y libboost${BOOST_VERSION}-all-dev \ + wcslib-dev \ + cmake cmake-doc cmake-curses-gui make \ + libgsl0-dev \ + python-matplotlib \ + python-sphinx \ + libcfitsio3-dev \ + python-numpy \ + num-utils \ + python-scipy \ + libblas-dev \ + python-sip-dev \ + openmpi-bin openmpi-common \ + ipython + +RUN cd $INSTALLDIR && svn co http://usg.lofar.org/svn/code/trunk lofarsoft && \ + export LOFARSOFT=${INSTALLDIR}/lofarsoft && . $LOFARSOFT/devel_common/scripts/init.sh && \ + cd $LOFARSOFT && ./bootstrap && cd build && cmake -DCASACORE_FROM_LATEST_SVN_REVISION=ON . && make rebuild_cache + +ENV LOFARSOFT=${INSTALLDIR}/lofarsoft + +RUN . $LOFARSOFT/devel_common/scripts/init.sh && cd $LOFARSOFT/build && make hdf5 + +RUN . $LOFARSOFT/devel_common/scripts/init.sh && cd $LOFARSOFT/build && make dal1 + + + +# +# config +# +COPY bashrc /opt/bashrc + +# +# entry +# +COPY chuser.sh /usr/local/bin/chuser.sh +WORKDIR /home/${USER} +ENTRYPOINT ["sudo","-E","/usr/local/bin/chuser.sh"] + diff --git a/Docker/lofar-tbbwriter/bashrc b/Docker/lofar-tbbwriter/bashrc new file mode 100644 index 0000000000000000000000000000000000000000..66b9cd7d8e99863c3534dc27cfcba6c18b6cf9c3 --- /dev/null +++ b/Docker/lofar-tbbwriter/bashrc @@ -0,0 +1,11 @@ +#!/bin/bash + +# lofar +[ -r ${INSTALLDIR}/lofar/lofarinit.sh ] && source ${INSTALLDIR}/lofar/lofarinit.sh +export PATH PYTHONPATH LD_LIBRARY_PATH LOFARROOT + +# qpid +#source ${INSTALLDIR}/qpid/.profile + +# lofarsoft +source ${LOFARSOFT}/devel_common/scripts/init.sh diff --git a/Docker/lofar-tbbwriter/chuser.sh b/Docker/lofar-tbbwriter/chuser.sh new file mode 100755 index 0000000000000000000000000000000000000000..3c4e9d3ab0fa826b4dc5804f756e241958103004 --- /dev/null +++ b/Docker/lofar-tbbwriter/chuser.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Fetch the user name used in this container +export USER=${SUDO_USER} + +if [ -n "${LUSER}" ]; then + if [ -z "${LGROUP}" ]; then + LGROUP=${LUSER} + fi + + OLDID=`id -u ${USER}` + + # Replace USER by LUSER:LGROUP + sed -i -e "s/${USER}:x:[0-9]\+:[0-9]\+/${USER}:x:${LUSER}:${LGROUP}/g" /etc/passwd + sed -i -e "s/${USER}:x:[0-9]\+:/${USER}:x:${LGROUP}:/g" /etc/group + + # Set ownership of home dir to new user + chown --from=${OLDID} -R ${LUSER}:${LGROUP} ${HOME} + + # Set ownership of installed software to new user + chown --from=${OLDID} -R ${LUSER}:${LGROUP} /opt +fi + +# Update environment for updated user +export HOME=/home/${USER} + +# Import bashrc for software in /opt +source /opt/bashrc + +# Use exec to make sure we propagate signals +# `env' is needed to propagate PATH variables through sudo. +exec sudo -u ${USER} -E env "PATH=$PATH" "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" "PYTHONPATH=$PYTHONPATH" "$@" diff --git a/INSTALL b/INSTALL index e6ec66a18b2b828f9d95fcd32af1227fdcaf48ce..5cfd1ae8eec2578b7abbd27ea3aa85bbb162a43e 100644 --- a/INSTALL +++ b/INSTALL @@ -14,9 +14,11 @@ Supported Systems Install from Ubuntu Packages ---------------------------- -To install LOFAR Offline processing tools the easy way for a specific Ubuntu version, see: +To install LOFAR Offline processing tools (and many more radio astro packages) +the easy way for Ubuntu LTS version(s), see: - https://launchpad.net/~radio-astro/+archive/ubuntu/main + KERN: The Radio Astronomy Software Suite + http://kernsuite.info/ Install using Docker Scripts @@ -26,6 +28,10 @@ To create a Docker container with Ubuntu or CentOS and LOFAR Offline processing https://github.com/lofar-astron/lofar-deploy +or, again for Ubuntu LTS: + + http://kernsuite.info/ + Dependencies for Manual Build from Source ----------------------------------------- @@ -37,11 +43,9 @@ package manager. For almost all cases, you also need the development packages For Debian/Ubuntu (likely incomplete list, but goes a long way): apt-get install subversion cmake make g++ gfortran python-dev python-numpy - apt-get install libboost-dev libblitz0-dev libfftw3-dev libcfitsio3-dev libxml2-dev - apt-get install libpng-dev # for AOFlagger + apt-get install libboost-dev libblitz0-dev libfftw3-dev libcfitsio3-dev libxml2-dev liblog4cplus-dev + apt-get install libpng12-dev # or latest version; for AOFlagger apt-get install libreadline-dev libpqxx-dev doxygen # optional - apt-get install libgsl0-dev # optional, for AOFlagger's RayleighFitter - apt-get install libgtkmm-[2.4|3.0]-dev libsigc++-2.0-dev # optional, for AOFlagger's rficonsole apt-get install libunittest++-dev # optional, for tests that use UnitTest++ @@ -49,10 +53,7 @@ For CentOS/Fedora (likely incomplete list, but goes a long way): yum install subversion cmake make gcc python-devel numpy yum install boost-devel fftw-devel cfitsio-devel libxml2-devel libpng-devel - yum install readline-devel libpqxx-devel doxygen # optional - yum install gsl-devel # optional, for AOFlagger's RayleighFitter - yum install gtkmm[24|30]-devel libsigc++20-devel # optional, for AOFlagger's rficonsole The blitz and blitz-devel pkgs are available on some distros, but not on e.g. CentOS 7. If required for your build, but not in your repo, take the two RPMs from CentOS 6 (or build from source). @@ -62,14 +63,17 @@ For CentOS/Fedora (likely incomplete list, but goes a long way): -NOTE (any OS): For manual builds, the most prominent dependency you likely need to build is casacore (+ friends): +NOTE (any OS): For manual builds, the most prominent dependencies you likely need to build are casacore (+ friends) and AOFlagger: casacore: https://github.com/casacore/casacore python-casacore ('pyrap'): https://github.com/casacore/python-casacore - IERS/measures tables: https://github.com/casacore/casacore-data-update - casarest: https://svn.astron.nl/casarest + IERS/measures tables: ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar (auto-updated weekly), + also see https://github.com/casacore/casacore-data-update + casarest: https://github.com/casacore/casarest - AOFlagger: http://sourceforge.net/projects/aoflagger/ + AOFlagger: https://sourceforge.net/projects/aoflagger/ + which may need: Debian/Ubuntu: libgsl0-dev libgtkmm-[2.4|3.0]-dev libsigc++-2.0-dev + CentOS/Fedora: gsl-devel gtkmm[24|30]-devel libsigc++20-devel Instructions for Manual Build from Source @@ -102,8 +106,8 @@ Instructions for Manual Build from Source All tests should pass, however, a few packages (not selected above) require GPU hardware or a database to pass all tests. -- You may want to add the installation path bin/ to your PATH: +- You may want to add the installation path bin/ to your PATH by sourcing (not executing!) the lofarinit script: - source "$HOME/local/$LOFAR_RELEASE/lofarinit.sh" # for Bourne-like shells, or - source "$HOME/local/$LOFAR_RELEASE/lofarinit.csh" # for C-like shells + . "$HOME/local/$LOFAR_RELEASE/lofarinit.sh" # for Bourne-like shells, or + . "$HOME/local/$LOFAR_RELEASE/lofarinit.csh" # for C-like shells diff --git a/LCS/ApplCommon/CMakeLists.txt b/LCS/ApplCommon/CMakeLists.txt index 2541f284bfb15a1d591bc22a082a85fff28a3c60..6f563d1af0b3e1785ba7890ccc6c91902ab470c7 100644 --- a/LCS/ApplCommon/CMakeLists.txt +++ b/LCS/ApplCommon/CMakeLists.txt @@ -3,7 +3,7 @@ lofar_package(ApplCommon 3.1 DEPENDS Common) include(LofarFindPackage) -lofar_find_package(Boost COMPONENTS date_time regex) +lofar_find_package(Boost COMPONENTS date_time regex REQUIRED) add_subdirectory(include/ApplCommon) add_subdirectory(src) diff --git a/LCS/Common/include/Common/CMakeLists.txt b/LCS/Common/include/Common/CMakeLists.txt index 5761963f8087315a3db407c7e76c4afa4768008c..90b424ba9c931a669c0372a8680a3ae79c9db4d8 100644 --- a/LCS/Common/include/Common/CMakeLists.txt +++ b/LCS/Common/include/Common/CMakeLists.txt @@ -24,6 +24,7 @@ install(FILES FileLocator.h hexdump.h InputParSet.h + IOPriority.h i4complex.h KVpair.h lofar_algorithm.h @@ -93,6 +94,7 @@ install(FILES DESTINATION include/${PACKAGE_NAME}/shmem) install(FILES + Thread/Barrier.h Thread/Cancellation.h Thread/Condition.h Thread/Mutex.h diff --git a/RTCP/Cobalt/OutputProc/src/IOPriority.h b/LCS/Common/include/Common/IOPriority.h similarity index 87% rename from RTCP/Cobalt/OutputProc/src/IOPriority.h rename to LCS/Common/include/Common/IOPriority.h index 66eb128e9acb4e22bb2dd9e0a3a4f9f8919c1912..8a367ff57780a2e3d3d71906e63c100d9d132e9d 100644 --- a/RTCP/Cobalt/OutputProc/src/IOPriority.h +++ b/LCS/Common/include/Common/IOPriority.h @@ -1,5 +1,6 @@ -//# IOPriority.h: define some Linux specific IO priority macro's -//# Copyright (C) 2011-2013 ASTRON (Netherlands Institute for Radio Astronomy) +//# IOPriority.h: Linux specific priority functions +//# Copyright (C) 2011-2013, 2016 +//# ASTRON (Netherlands Institute for Radio Astronomy) //# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands //# //# This file is part of the LOFAR software suite. @@ -18,8 +19,8 @@ //# //# $Id$ -#ifndef LOFAR_STORAGE_IOPRIORITY_H -#define LOFAR_STORAGE_IOPRIORITY_H +#ifndef LOFAR_COMMON_IOPRIORITY_H +#define LOFAR_COMMON_IOPRIORITY_H #define IOPRIO_BITS (16) #define IOPRIO_CLASS_SHIFT (13) @@ -44,6 +45,9 @@ #include <linux/version.h> #endif +namespace LOFAR +{ + enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, @@ -85,9 +89,13 @@ inline int ioprio_get(int which, int who) #endif } -inline void setIOpriority() +// realTime true requires CAP_SYS_ADMIN. false does not require a capability, +// but does set to the more favorable prio 0 with IOPRIO_CLASS_BE (default 4). +inline void setIOpriority(bool realTime) { - if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT,7)) != 0) { + if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), + realTime ? IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7) : + IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0)) != 0) { switch (errno) { case EPERM: { @@ -172,5 +180,7 @@ inline void lockInMemory(rlim_t memLockLimit = RLIM_INFINITY) } } +} // namespace LOFAR + #endif diff --git a/LCS/Common/include/Common/Thread/Barrier.h b/LCS/Common/include/Common/Thread/Barrier.h new file mode 100644 index 0000000000000000000000000000000000000000..c3bb891b540dc66209383df74924cb798ded541c --- /dev/null +++ b/LCS/Common/include/Common/Thread/Barrier.h @@ -0,0 +1,78 @@ +//# Barrier.h: thread synchronization barrier +//# +//# Copyright (C) 2016 +//# ASTRON (Netherlands Foundation for Research in Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, softwaresupport@astron.nl +//# +//# This program is free software; you can redistribute it and/or modify +//# it under the terms of the GNU General Public License as published by +//# the Free Software Foundation; either version 2 of the License, or +//# (at your option) any later version. +//# +//# This program is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License +//# along with this program; if not, write to the Free Software +//# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//# +//# $Id$ + +#ifndef LOFAR_LCS_COMMON_BARRIER_H +#define LOFAR_LCS_COMMON_BARRIER_H + +#ifdef USE_THREADS + +#include <pthread.h> +#include <Common/LofarLogger.h> +#include <Common/SystemCallException.h> + +namespace LOFAR { + +class Barrier { +public: + explicit Barrier(unsigned count) + { + int rv = pthread_barrier_init(&bar, NULL, count); + if (rv != 0) { + throw SystemCallException("pthread_barrier_init", rv, THROW_ARGS); + } + } + + ~Barrier() + { + int rv = pthread_barrier_destroy(&bar); + if (rv != 0) { + // get backtrace w/out stack unwinding from destr (could be done w/out exc) + try { + throw SystemCallException("pthread_barrier_destroy", rv, THROW_ARGS); + } catch (SystemCallException &exc) { + LOG_ERROR_STR("pthread_barrier_destroy() failed: " << exc.what()); + } + } + } + + void wait() + { + int rv = pthread_barrier_wait(&bar); + if (rv != 0 && rv != PTHREAD_BARRIER_SERIAL_THREAD) { + throw SystemCallException("pthread_barrier_wait", rv, THROW_ARGS); + } + } + +private: + pthread_barrier_t bar; + + // don't use + Barrier(); + Barrier(const Barrier& ); // cannot wait or destroy on a copy + Barrier& operator=(const Barrier& ); // idem +}; + +} // namespace LOFAR + +#endif + +#endif diff --git a/LCS/Common/include/Common/Thread/Queue.h b/LCS/Common/include/Common/Thread/Queue.h index 963978a51a5a6dcd1a2396a195750b2c5d457b63..dd8ad576fffb3eb3ae100eeb3db17cfa0ecee9fb 100644 --- a/LCS/Common/include/Common/Thread/Queue.h +++ b/LCS/Common/include/Common/Thread/Queue.h @@ -28,7 +28,7 @@ #include <Common/Thread/Condition.h> #include <Common/Thread/Mutex.h> -#include <list> +#include <deque> #include <time.h> @@ -61,7 +61,7 @@ template <typename T> class Queue mutable Mutex itsMutex; Condition itsNewElementAppended; - std::list<T> itsQueue; + std::deque<T> itsQueue; }; diff --git a/LCS/Common/src/AddressTranslator.cc b/LCS/Common/src/AddressTranslator.cc index 0c916d141f58e756a37804dbf1e53cae47829a98..01b33449e4c30fec094032b3cb49da98f2a34f99 100644 --- a/LCS/Common/src/AddressTranslator.cc +++ b/LCS/Common/src/AddressTranslator.cc @@ -46,8 +46,8 @@ namespace LOFAR { // Map of symbol tables. // Use the load address of the shared object or executable as key. - typedef std::map< bfd_vma, boost::shared_ptr<SymbolTable> > SymbolTableMap; - + typedef boost::shared_ptr<SymbolTable> SymbolTablePtr; + typedef std::map<bfd_vma, SymbolTablePtr> SymbolTableMap; // The map of symbol tables is implemented as a Meyers singleton. // Use a lock to make access thread-safe. SymbolTableMap& theSymbolTableMap() @@ -88,8 +88,9 @@ namespace LOFAR // if the symbol table of #bfdFile is not yet present in the map. SymbolTableMap::iterator it = theSymbolTableMap().find(base_addr); if(it == theSymbolTableMap().end()) { - it = theSymbolTableMap().insert - (std::make_pair(base_addr, new SymbolTable(bfdFile))).first; + it = theSymbolTableMap().insert( + std::make_pair(base_addr, + SymbolTablePtr(new SymbolTable(bfdFile)))).first; } // Get the BFD-handle of the matching SymbolTable object. diff --git a/LCS/Common/src/ReadLine.cc b/LCS/Common/src/ReadLine.cc index 752f9325d25dc2f31d525ee5619b61834ae1ee29..75de0414c489e43c488587ca6ac1504338f8c6e8 100644 --- a/LCS/Common/src/ReadLine.cc +++ b/LCS/Common/src/ReadLine.cc @@ -46,7 +46,7 @@ namespace LOFAR { if (!prompt.empty()) cerr << prompt; getline (cin, line); - return cin; + return (bool)cin; } #endif diff --git a/LCS/Common/test/CMakeLists.txt b/LCS/Common/test/CMakeLists.txt index f645d0063d138f3f8997e54d85a4dc34c011e2f1..95266a86df322e976bfb9235d5808cb51f3e7c9e 100644 --- a/LCS/Common/test/CMakeLists.txt +++ b/LCS/Common/test/CMakeLists.txt @@ -2,6 +2,7 @@ include(LofarCTest) +lofar_add_test(tBarrier tBarrier.cc) lofar_add_test(tBoostBitset tBoostBitset.cc) lofar_add_test(testLogger testLogger.cc) lofar_add_test(tCasaLogSink tCasaLogSink.cc) diff --git a/LCS/Common/test/tBarrier.cc b/LCS/Common/test/tBarrier.cc new file mode 100644 index 0000000000000000000000000000000000000000..774d57065a6f6e46a431989619191aba7da37260 --- /dev/null +++ b/LCS/Common/test/tBarrier.cc @@ -0,0 +1,98 @@ +//# tBarrier.cc: Test program for thread synchronization barrier +//# +//# Copyright (C) 2016 +//# ASTRON (Netherlands Institute for Radio Astronomy) +//# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +//# +//# This file is part of the LOFAR software suite. +//# The LOFAR software suite is free software: you can redistribute it and/or +//# modify it under the terms of the GNU General Public License as published +//# by the Free Software Foundation, either version 3 of the License, or +//# (at your option) any later version. +//# +//# The LOFAR software suite is distributed in the hope that it will be useful, +//# but WITHOUT ANY WARRANTY; without even the implied warranty of +//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//# GNU General Public License for more details. +//# +//# You should have received a copy of the GNU General Public License along +//# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +//# +//# $Id$ + +#include <lofar_config.h> + +#include <unistd.h> // alarm(3) +#include <Common/Thread/Thread.h> +#include <Common/Thread/Barrier.h> + +using namespace std; +using namespace LOFAR; + +static void testTrivial() +{ +#ifdef USE_THREADS + Barrier bar(1); + bar.wait(); +#endif +} + +#ifdef USE_THREADS +static Barrier barTestUse(4); // for 3 thread + main thread + +struct Thr { + Thr() : thread(this, &Thr::f) { } + +private: + void f() { + barTestUse.wait(); + barTestUse.wait(); + barTestUse.wait(); + } + + Thread thread; +}; +#endif + +static void testUse() +{ +#ifdef USE_THREADS + Thr t1; + Thr t2; + Thr t3; + + barTestUse.wait(); + barTestUse.wait(); + barTestUse.wait(); +#endif +} + +static void testError() +{ +#ifdef USE_THREADS + int exc = 0; + + // count 0 is invalid + try { + Barrier(0); + } catch (Exception& ) { + exc = 1; + } + + ASSERT(exc == 1); +#endif +} + + +int main() +{ + INIT_LOGGER("tBarrier"); + + alarm(10); // don't wait until ctest timeout on deadlock + + testTrivial(); + testUse(); + testError(); + + return 0; +} diff --git a/LCS/Common/test/tQueue.cc b/LCS/Common/test/tQueue.cc index 4bed1d72f5d4a13c26dba13af10632f3c2665095..aebb45a20ad8105103ec0f5c28f6dfd1ea98a89e 100644 --- a/LCS/Common/test/tQueue.cc +++ b/LCS/Common/test/tQueue.cc @@ -63,7 +63,7 @@ public: void mainLoop() { sleep(1); // make "sure" B blocks on q.remove() - for (int i = 0; i < 10; i++) + for (int i = 0; i < 1024*1024; i++) q.append(i); } }; @@ -71,7 +71,7 @@ public: class B { public: void mainLoop() { - for (int i = 0; i < 10; i++) + for (int i = 0; i < 1024*1024; i++) ASSERT( q.remove() == i ); } }; diff --git a/LCS/Common/test/tgetparsetvalue.run b/LCS/Common/test/tgetparsetvalue.run index af7baec8378e4fc7bcb93321863a44ac3970545d..0a535cae12b5c86d89645a3f039b33c50a3ca082 100755 --- a/LCS/Common/test/tgetparsetvalue.run +++ b/LCS/Common/test/tgetparsetvalue.run @@ -1,15 +1,17 @@ #!/bin/sh -../src/getparsetvalue tgetparsetvalue.parset key1 -../src/getparsetvalue tgetparsetvalue.parset key1 0 -../src/getparsetvalue tgetparsetvalue.parset key1 1 -../src/getparsetvalue tgetparsetvalue.parset key1 2 -../src/getparsetvalue tgetparsetvalue.parset key1 3 -../src/getparsetvalue tgetparsetvalue.parset key1 -1 -../src/getparsetvalue tgetparsetvalue.parset key1 -2 -../src/getparsetvalue tgetparsetvalue.parset key1 -3 -../src/getparsetvalue tgetparsetvalue.parset key1 -4 -../src/getparsetvalue tgetparsetvalue.parset key1.sub1 +PATH=$(cd ../src && pwd):$PATH -../src/getparsetvalue tgetparsetvalue.parset key0 -../src/getparsetvalue -d abc -d efgh tgetparsetvalue.parset key0 +getparsetvalue tgetparsetvalue.parset key1 +getparsetvalue tgetparsetvalue.parset key1 0 +getparsetvalue tgetparsetvalue.parset key1 1 +getparsetvalue tgetparsetvalue.parset key1 2 +getparsetvalue tgetparsetvalue.parset key1 3 +getparsetvalue tgetparsetvalue.parset key1 -1 +getparsetvalue tgetparsetvalue.parset key1 -2 +getparsetvalue tgetparsetvalue.parset key1 -3 +getparsetvalue tgetparsetvalue.parset key1 -4 +getparsetvalue tgetparsetvalue.parset key1.sub1 + +getparsetvalue tgetparsetvalue.parset key0 +getparsetvalue -d abc -d efgh tgetparsetvalue.parset key0 diff --git a/LCS/MessageBus/qpid/local/sbin/build_qpid b/LCS/MessageBus/qpid/local/sbin/build_qpid index fc344947c888ae8a953fd8c0b6c3349837eed132..1ae7b21324b4371b8beefff64e34c963ae701516 100755 --- a/LCS/MessageBus/qpid/local/sbin/build_qpid +++ b/LCS/MessageBus/qpid/local/sbin/build_qpid @@ -1,16 +1,24 @@ -#!/bin/bash -QPIDINSTALLDIR=/opt/qpid +#!/bin/bash -eu + +# set default configuation +: ${QPIDINSTALLDIR:=/opt/qpid} +: ${PROTONVERSION:=0.8} +: ${QPIDVERSION:=0.34} # checkout sources for apache qpid and apache proton if [[ -x ~/sources/proton ]] then echo Skipping svn download because source directory exists else - cd ~ - mkdir -p sources + mkdir -p ~/sources cd ~/sources - svn co http://svn.apache.org/repos/asf/qpid/proton/tags/0.8/ proton - svn co http://svn.apache.org/repos/asf/qpid/tags/0.30/qpid/ qpid-0.30 + svn export http://svn.apache.org/repos/asf/qpid/proton/tags/$PROTONVERSION/ proton + if test ${QPIDVERSION} ">" "0.32"; then + QPIDTAG=qpid-cpp-$QPIDVERSION + else + QPIDTAG=$QPIDVERSION + fi + svn export http://svn.apache.org/repos/asf/qpid/tags/$QPIDTAG/qpid/ qpid-$QPIDVERSION fi # build and install proton libraries cd ~/sources/proton/ @@ -33,7 +41,7 @@ fi PROTONDIR=$QPIDINSTALLDIR/lib/cmake/Proton # build and install QPID C++ broker and libraries -cd ~/sources/qpid-0.30/cpp +cd ~/sources/qpid-$QPIDVERSION/cpp rm -Rf ./BUILD mkdir BUILD cd BUILD @@ -46,8 +54,8 @@ make -j4 make install # setup config with 256MB storage per queue max. -cat >> $QPIDINSTALLDIR/etc/qpid/qpidd.conf << \EOF - +mkdir -p $QPIDINSTALLDIR/etc/qpid +cat > $QPIDINSTALLDIR/etc/qpid/qpidd.conf << \EOF # max 256MB per queue persistent buffering num-jfiles=32 jfile-size-pgs=128 @@ -55,19 +63,19 @@ jfile-size-pgs=128 EOF # build and install QPID python generic libs -cd ~/sources/qpid-0.30/python +cd ~/sources/qpid-$QPIDVERSION/python ./setup.py build ./setup.py install --home=$QPIDINSTALLDIR cd .. # build and install QPID QMF python libraries -cd ~/sources/qpid-0.30/extras/qmf +cd ~/sources/qpid-$QPIDVERSION/extras/qmf ./setup.py build ./setup.py install --home=$QPIDINSTALLDIR cd ../.. # build and install QPID tools -cd ~/sources/qpid-0.30/tools +cd ~/sources/qpid-$QPIDVERSION/tools ./setup.py build ./setup.py install --home=$QPIDINSTALLDIR cd .. diff --git a/LCS/MessageDaemons/ObservationStartListener/src/lofarlogger.sh b/LCS/MessageDaemons/ObservationStartListener/src/lofarlogger.sh index 032accef1d65be8ee803ce3d1096f3907efa73f5..cb1c9b595c45afd81f1bc8e3cb2efa703c3f88aa 100755 --- a/LCS/MessageDaemons/ObservationStartListener/src/lofarlogger.sh +++ b/LCS/MessageDaemons/ObservationStartListener/src/lofarlogger.sh @@ -1,6 +1,6 @@ # lofarlogger.sh # -# Copyright (C) 2015 +# Copyright (C) 2015-2016 # ASTRON (Netherlands Institute for Radio Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands # @@ -22,12 +22,17 @@ # Usage: source lofarlogger.sh # then e.g.: log INFO "foo bar" -# logs e.g.: 2015-10-16 16:00:46,186 INFO foo bar +# logs e.g.: 2015-10-16 16:00:46,186 INFO - foo bar log() { - loglevel=$1 # one of: DEBUG INFO WARNING ERROR CRITICAL + loglevel=$1 # one of: DEBUG INFO WARN ERROR FATAL message=$2 ts=`date --utc '+%F %T,%3N'` # e.g. 2015-10-16 16:00:46,186 - echo "$ts $loglevel $message" >&2 + echo "$ts $loglevel - $message" >&2 +} + +fatal() { + log $1 + exit 1 } diff --git a/LCS/Messaging/python/messaging/RPC.py b/LCS/Messaging/python/messaging/RPC.py index f67f4c86c382b8958b7208ba4de107b10965902b..81a94cc05bc0544cb21be93de700d9190db8cae2 100644 --- a/LCS/Messaging/python/messaging/RPC.py +++ b/LCS/Messaging/python/messaging/RPC.py @@ -62,6 +62,10 @@ class RPCException(Exception): "Exception occured in the RPC code itself, like time-out, invalid message received, etc." pass +class RPCTimeoutException(RPCException): + "Exception occured when the RPC call times out." + pass + class RPC(): """ This class provides an easy way to invoke a Remote Rrocedure Call to a @@ -151,6 +155,9 @@ class RPC(): Reply = FromBus("%s ; %s" %(ReplyAddress,str(options)), broker=self.broker) else: Reply = FromBus("%s/%s" % (self.BusName, ReplyAddress), broker=self.broker) + # supply fully specified reply address including '{node:{type:topic}}' specification so handlers like JMS can handle reply address + ReplyAddress = "%s/%s ;{node:{type:topic}}" % (self.BusName, ReplyAddress) + with Reply: MyMsg = RequestMessage(content=Content, reply_to=ReplyAddress, has_args=HasArgs, has_kwargs=HasKwArgs) MyMsg.ttl = timeout @@ -163,7 +170,7 @@ class RPC(): status["state"] = "TIMEOUT" status["errmsg"] = "RPC Timed out" status["backtrace"] = "" - raise RPCException(status) + raise RPCTimeoutException(status) # Check for illegal message type if isinstance(answer, ReplyMessage) is False: @@ -193,7 +200,7 @@ class RPC(): excep_mod = __import__("exceptions") excep_class_ = getattr(excep_mod, answer.errmsg.split(':')[0], None) if (excep_class_ != None): - instance = excep_class_(answer.backtrace) + instance = excep_class_("%s%s" % (answer.errmsg.split(':',1)[1].strip(), answer.backtrace)) raise (instance) else: raise RPCException(answer.errmsg) @@ -282,11 +289,13 @@ class RPCWrapper(object): def __init__(self, busname=None, servicename=None, broker=None, - timeout=10): + timeout=10, + verbose=False): self.busname = busname self.servicename = servicename self.broker = broker self.timeout = timeout + self.verbose = verbose self._serviceRPCs = {} #cache of rpc's for each service @@ -313,7 +322,8 @@ class RPCWrapper(object): '''execute the rpc call on the <bus>/<service>.<method> and return the result''' try: if self.timeout: - rpckwargs = {'timeout': self.timeout} + rpckwargs = {'timeout': self.timeout, + 'Verbose': self.verbose} service_method = (self.servicename + '.' + method) if self.servicename and method \ else self.servicename if self.servicename else method diff --git a/LCS/Messaging/python/messaging/test/t_messagebus.run b/LCS/Messaging/python/messaging/test/t_messagebus.run index a64fd56ccd2c11c40b771f6cfb90ebda604483ab..28770b2f76b019ec5dec54e479c84f54e62a7300 100755 --- a/LCS/Messaging/python/messaging/test/t_messagebus.run +++ b/LCS/Messaging/python/messaging/test/t_messagebus.run @@ -1,14 +1,20 @@ #!/bin/bash -e +PYTHONVERSION=$(python -V|awk '{print $1}') +if [ $PYTHONVERSION \> "2.6.9" ] ; then -# Cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM -trap 'qpid-config del queue --force $queue' 0 1 2 3 15 + # Cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM + trap 'qpid-config del queue --force $queue' 0 1 2 3 15 -# Generate randome queue name -queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) + # Generate randome queue name + queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) -# Create the queue -qpid-config add queue $queue + # Create the queue + qpid-config add queue $queue + + # Run the unit test + source python-coverage.sh + python_coverage_test "Messaging/python" t_messagebus.py $queue +else + echo "Python version to low fo ctests" +fi -# Run the unit test -source python-coverage.sh -python_coverage_test "Messaging/python" t_messagebus.py $queue diff --git a/LCS/Messaging/python/messaging/test/t_messages.run b/LCS/Messaging/python/messaging/test/t_messages.run index 2d82773f1b7a6a80b89bbd455a43c7757aeb2fec..f0c8f144c090a91c89764de82f2a3ff812e116ae 100755 --- a/LCS/Messaging/python/messaging/test/t_messages.run +++ b/LCS/Messaging/python/messaging/test/t_messages.run @@ -2,4 +2,10 @@ # Run the unit test source python-coverage.sh -python_coverage_test "Messaging/python" t_messages.py +PYTHONVERSION=$(python -V|awk '{print $1}') +if [ $PYTHONVERSION \> "2.6.9" ] ; then + python_coverage_test "Messaging/python" t_messages.py +else + echo "python version too low for testing" +fi + diff --git a/LCS/Messaging/python/messaging/test/t_service_message_handler.run b/LCS/Messaging/python/messaging/test/t_service_message_handler.run index da523ee4c86ef5a53b1f9c31053b2da4fb79a1c9..9bc516a58491d04e5b1fcae698d5b43ed3ba8837 100755 --- a/LCS/Messaging/python/messaging/test/t_service_message_handler.run +++ b/LCS/Messaging/python/messaging/test/t_service_message_handler.run @@ -1,14 +1,20 @@ #!/bin/bash -e +PYTHONVERSION=$(python -V|awk '{print $1}') +if [ $PYTHONVERSION \> "2.6.9" ] ; then -#cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM -trap 'qpid-config del exchange --force $queue' 0 1 2 3 15 + #cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM + trap 'qpid-config del exchange --force $queue' 0 1 2 3 15 -# Generate randome queue name -queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) + # Generate randome queue name + queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) -# Create the queue -qpid-config add exchange topic $queue + # Create the queue + qpid-config add exchange topic $queue -# Run the unit test -source python-coverage.sh -python_coverage_test "Messaging/python" t_service_message_handler.py $queue + # Run the unit test + source python-coverage.sh + python_coverage_test "Messaging/python" t_service_message_handler.py $queue + +else + echo "Python version too low" +fi diff --git a/LCS/PyCommon/CMakeLists.txt b/LCS/PyCommon/CMakeLists.txt index c3803fdaec5f5032ec5480b41bf087cae392250b..7ab73b50fc58d930bd66d0c962c9d04298fb5250 100644 --- a/LCS/PyCommon/CMakeLists.txt +++ b/LCS/PyCommon/CMakeLists.txt @@ -5,14 +5,15 @@ lofar_package(PyCommon 1.0) lofar_find_package(Python 2.6 REQUIRED) include(PythonInstall) -add_subdirectory(test) - set(_py_files __init__.py dbcredentials.py factory.py + methodtrigger.py util.py postgres.py datetimeutils.py) python_install(${_py_files} DESTINATION lofar/common) + +add_subdirectory(test) diff --git a/LCS/PyCommon/methodtrigger.py b/LCS/PyCommon/methodtrigger.py new file mode 100644 index 0000000000000000000000000000000000000000..800d8d11ea19f00c3c7f1bb0cad9d6f2801c3faf --- /dev/null +++ b/LCS/PyCommon/methodtrigger.py @@ -0,0 +1,68 @@ +from threading import Lock, Condition + +__all__ = ["MethodTrigger"] + +class MethodTrigger: + """ + Set a flag when a specific method is called, possibly asynchronously. Caller can wait on this flag. + + Example: + + class Foo(object): + def bar(self): + pass + + foo = Foo() + trigger = MethodTrigger(foo, "bar") + + if trigger.wait(): # Waits for 10 seconds for foo.bar() to get called + print "foo.bar() got called" + else + # This will happen, as foo.bar() wasn't called + print "foo.bar() did not get called" + + Calls that were made before the trigger has been installed will not get recorded. + """ + + def __init__(self, obj, method): + assert isinstance(obj, object), "Object %s does not derive from object." % (obj,) + + self.obj = obj + self.method = method + self.old_func = obj.__getattribute__(method) + + self.called = False + self.args = [] + self.kwargs = {} + + self.lock = Lock() + self.cond = Condition(self.lock) + + # Patch the target method + obj.__setattr__(method, self.trigger) + + def trigger(self, *args, **kwargs): + # Save the call parameters + self.args = args + self.kwargs = kwargs + + # Call the original method + self.old_func(*args, **kwargs) + + # Restore the original method + self.obj.__setattr__(self.method, self.old_func) + + # Release waiting thread + with self.lock: + self.called = True + self.cond.notify() + + def wait(self, timeout=10.0): + # Wait for method to get called + with self.lock: + if self.called: + return True + + self.cond.wait(timeout) + + return self.called diff --git a/LCS/PyCommon/postgres.py b/LCS/PyCommon/postgres.py index 2db1316e19116db9630de7d6e86256a892a02fd0..1251ede07ad56b68ac27a017f19c81f47b0bea78 100644 --- a/LCS/PyCommon/postgres.py +++ b/LCS/PyCommon/postgres.py @@ -33,60 +33,34 @@ import psycopg2.extensions logger = logging.getLogger(__name__) -def makePostgresNotificationQueries(schema, table, action, view_for_row=None, view_selection_id=None): +def makePostgresNotificationQueries(schema, table, action, column_name='id'): action = action.upper() if action not in ('INSERT', 'UPDATE', 'DELETE'): raise ValueError('''trigger_type '%s' not in ('INSERT', 'UPDATE', 'DELETE')''' % action) - if view_for_row and action == 'DELETE': - raise ValueError('You cannot use a view for results on action DELETE') - - if view_for_row: - change_name = '''{table}_{action}_with_{view_for_row}'''.format(schema=schema, - table=table, - action=action, - view_for_row=view_for_row) - function_name = '''NOTIFY_{change_name}'''.format(change_name=change_name) - function_sql = ''' - CREATE OR REPLACE FUNCTION {schema}.{function_name}() - RETURNS TRIGGER AS $$ - DECLARE - new_row_from_view {schema}.{view_for_row}%ROWTYPE; - BEGIN - select * into new_row_from_view from {schema}.{view_for_row} where {view_selection_id} = NEW.id LIMIT 1; - PERFORM pg_notify(CAST('{change_name}' AS text), - '{{"old":' || {old} || ',"new":' || row_to_json(new_row_from_view)::text || '}}'); - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - '''.format(schema=schema, - function_name=function_name, - table=table, - action=action, - old='row_to_json(OLD)::text' if action == 'UPDATE' or action == 'DELETE' else '\'null\'', - view_for_row=view_for_row, - view_selection_id=view_selection_id if view_selection_id else 'id', - change_name=change_name.lower()) - else: - change_name = '''{table}_{action}'''.format(table=table, action=action) - function_name = '''NOTIFY_{change_name}'''.format(change_name=change_name) - function_sql = ''' - CREATE OR REPLACE FUNCTION {schema}.{function_name}() - RETURNS TRIGGER AS $$ - BEGIN - PERFORM pg_notify(CAST('{change_name}' AS text), - '{{"old":' || {old} || ',"new":' || {new} || '}}'); - RETURN {value}; - END; - $$ LANGUAGE plpgsql; - '''.format(schema=schema, - function_name=function_name, - table=table, - action=action, - old='row_to_json(OLD)::text' if action == 'UPDATE' or action == 'DELETE' else '\'null\'', - new='row_to_json(NEW)::text' if action == 'UPDATE' or action == 'INSERT' else '\'null\'', - value='OLD' if action == 'DELETE' else 'NEW', - change_name=change_name.lower()) + change_name = '''{table}_{action}'''.format(table=table, action=action) + if column_name != 'id': + change_name += '_column_' + column_name + function_name = '''NOTIFY_{change_name}'''.format(change_name=change_name) + function_sql = ''' + CREATE OR REPLACE FUNCTION {schema}.{function_name}() + RETURNS TRIGGER AS $$ + DECLARE payload text; + BEGIN + {begin_update_check}SELECT CAST({column_value} AS text) INTO payload; + PERFORM pg_notify(CAST('{change_name}' AS text), payload);{end_update_check} + RETURN {value}; + END; + $$ LANGUAGE plpgsql; + '''.format(schema=schema, + function_name=function_name, + table=table, + action=action, + column_value=('OLD' if action == 'DELETE' else 'NEW') + '.' + column_name, + value='OLD' if action == 'DELETE' else 'NEW', + change_name=change_name.lower(), + begin_update_check='IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN\n' if action == 'UPDATE' else '', + end_update_check='\nEND IF;' if action == 'UPDATE' else '') trigger_name = 'TRIGGER_NOTIFY_%s' % function_name diff --git a/LCS/PyCommon/test/CMakeLists.txt b/LCS/PyCommon/test/CMakeLists.txt index a2abf73a98a57ed76555c5beb9d870804ff264b8..79c9b43bfa8f22c32c1e1d4278b3ff298192a581 100644 --- a/LCS/PyCommon/test/CMakeLists.txt +++ b/LCS/PyCommon/test/CMakeLists.txt @@ -7,3 +7,4 @@ file(COPY DESTINATION ${CMAKE_BINARY_DIR}/bin) lofar_add_test(t_dbcredentials) +lofar_add_test(t_methodtrigger) diff --git a/LCS/PyCommon/test/t_dbcredentials.py b/LCS/PyCommon/test/t_dbcredentials.py index b5e8db69c457c5cabdec33f79c5e48b486ec74d3..8c2d131d12773e506bf1e27d6601b899706966ef 100644 --- a/LCS/PyCommon/test/t_dbcredentials.py +++ b/LCS/PyCommon/test/t_dbcredentials.py @@ -95,7 +95,7 @@ database = mydb def main(argv): - unittest.main(verbosity=2) + unittest.main() if __name__ == "__main__": # run all tests diff --git a/LCS/PyCommon/test/t_methodtrigger.py b/LCS/PyCommon/test/t_methodtrigger.py new file mode 100644 index 0000000000000000000000000000000000000000..a7562ad5ac57acc17c8d1aec1e3cbe1d5a62219e --- /dev/null +++ b/LCS/PyCommon/test/t_methodtrigger.py @@ -0,0 +1,124 @@ +import unittest +from lofar.common.methodtrigger import MethodTrigger + +from threading import Thread +import time + +class TestMethodTrigger(unittest.TestCase): + def setUp(self): + # Create a basic object + class TestClass(object): + def func(self): + pass + + self.testobj = TestClass() + + # Install trigger + self.trigger = MethodTrigger(self.testobj, "func") + + def test_no_call(self): + """ Do not trigger. """ + + # Wait for trigger + self.assertFalse(self.trigger.wait(0.1)) + + def test_serial_call(self): + """ Trigger and wait serially. """ + + # Call function + self.testobj.func() + + # Wait for trigger + self.assertTrue(self.trigger.wait(0.1)) + + def test_parallel_call(self): + """ Trigger and wait in parallel. """ + + class wait_thread(Thread): + def __init__(self, trigger): + Thread.__init__(self) + self.result = None + self.trigger = trigger + + def run(self): + self.result = self.trigger.wait(1.0) + + class call_thread(Thread): + def __init__(self,func): + Thread.__init__(self) + self.func = func + + def run(self): + time.sleep(0.5) + self.func() + + # Start threads + t1 = wait_thread(self.trigger) + t1.start() + t2 = call_thread(self.testobj.func) + t2.start() + + # Wait for them to finish + t1.join() + t2.join() + + # Inspect result + self.assertTrue(t1.result) + +class TestArgs(unittest.TestCase): + def setUp(self): + # Create a basic object + class TestClass(object): + def func(self, a, b, c=None, d=None): + pass + + self.testobj = TestClass() + + # Install trigger + self.trigger = MethodTrigger(self.testobj, "func") + + def test_args(self): + """ Trigger and check args. """ + + # Call function + self.testobj.func(1, 2) + + # Wait for trigger + self.assertTrue(self.trigger.wait(0.1)) + + # Check stored arguments + self.assertEqual(self.trigger.args, (1, 2)) + + def test_kwargs(self): + """ Trigger and check kwargs. """ + + # Call function + self.testobj.func(a=1, b=2) + + # Wait for trigger + self.assertTrue(self.trigger.wait(0.1)) + + # Check stored arguments + self.assertEqual(self.trigger.kwargs, {"a": 1, "b": 2}) + + def test_full(self): + """ Trigger and check both args and kwargs. """ + + # Call function + self.testobj.func(1, 2, c=3, d=4) + + # Wait for trigger + self.assertTrue(self.trigger.wait(0.1)) + + # Check stored arguments + self.assertEqual(self.trigger.args, (1, 2)) + self.assertEqual(self.trigger.kwargs, {"c": 3, "d": 4}) + +def main(argv): + unittest.main() + +if __name__ == "__main__": + # run all tests + import sys + main(sys.argv[1:]) + diff --git a/LCS/PyCommon/test/t_methodtrigger.sh b/LCS/PyCommon/test/t_methodtrigger.sh new file mode 100755 index 0000000000000000000000000000000000000000..c786a2cbcdafae4571c5b6fba84fa21c96381ad6 --- /dev/null +++ b/LCS/PyCommon/test/t_methodtrigger.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runctest.sh t_methodtrigger diff --git a/LCS/Stream/include/Stream/CMakeLists.txt b/LCS/Stream/include/Stream/CMakeLists.txt index 854586b75227a02447db51483cb509deb47dc961..701f08b79b02258268b6ed7dcc55076dcd63426d 100644 --- a/LCS/Stream/include/Stream/CMakeLists.txt +++ b/LCS/Stream/include/Stream/CMakeLists.txt @@ -3,6 +3,7 @@ set (inst_HEADERS FileDescriptorBasedStream.h FileStream.h NamedPipeStream.h + NetFuncs.h NullStream.h PortBroker.h SharedMemoryStream.h diff --git a/LCS/Stream/src/NetFuncs.h b/LCS/Stream/include/Stream/NetFuncs.h similarity index 98% rename from LCS/Stream/src/NetFuncs.h rename to LCS/Stream/include/Stream/NetFuncs.h index 5c5aa59dee444f7b05a8f057288ec97b764dbab8..a71639f80b7a45d8ac6847bb3ad2ee97fc9df7a2 100644 --- a/LCS/Stream/src/NetFuncs.h +++ b/LCS/Stream/include/Stream/NetFuncs.h @@ -1,6 +1,6 @@ //# NetFuncs.h: //# -//# Copyright (C) 2008 +//# Copyright (C) 2015 //# ASTRON (Netherlands Institute for Radio Astronomy) //# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands //# diff --git a/LCS/Stream/include/Stream/SocketStream.h b/LCS/Stream/include/Stream/SocketStream.h index 5af1ad02bd812c919cb19ec3cfe265a5b6f62bc3..d26495c1337d105d00b09a192e2c41e03fc0ab50 100644 --- a/LCS/Stream/include/Stream/SocketStream.h +++ b/LCS/Stream/include/Stream/SocketStream.h @@ -52,7 +52,9 @@ class SocketStream : public FileDescriptorBasedStream FileDescriptorBasedStream *detach(); void reaccept(time_t deadline = 0); // only for TCP server socket - void setReadBufferSize(size_t size); + + size_t getReadBufferSize() const; + void setReadBufferSize(size_t size) const; const Protocol protocol; const Mode mode; diff --git a/LCS/Stream/src/NetFuncs.cc b/LCS/Stream/src/NetFuncs.cc index d0c6179a1888c3be25556f07d40f5170af034427..a44bdf7e0290e8fc6b3e032b61beef45965832cd 100644 --- a/LCS/Stream/src/NetFuncs.cc +++ b/LCS/Stream/src/NetFuncs.cc @@ -19,9 +19,10 @@ //# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. //# //# $Id$ + #include <lofar_config.h> -#include "NetFuncs.h" +#include <Stream/NetFuncs.h> #include <Common/Thread/Mutex.h> #include <Common/LofarLogger.h> #include <Common/SystemCallException.h> diff --git a/LCS/Stream/src/SocketStream.cc b/LCS/Stream/src/SocketStream.cc index eacf2dd1560a3549d351ddad97c33eda1ba48516..8afb6968e398f398d56683ec353852994aa90ec6 100644 --- a/LCS/Stream/src/SocketStream.cc +++ b/LCS/Stream/src/SocketStream.cc @@ -45,7 +45,7 @@ #include <Common/Thread/Cancellation.h> #include <Common/LofarLogger.h> -#include "NetFuncs.h" +#include <Stream/NetFuncs.h> //# AI_NUMERICSERV is not defined on OS-X #ifndef AI_NUMERICSERV @@ -243,9 +243,24 @@ void SocketStream::accept(time_t deadline) } -void SocketStream::setReadBufferSize(size_t size) +size_t SocketStream::getReadBufferSize() const { - if (fd >= 0 && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof size) < 0) + size_t size; + socklen_t sizeSize = sizeof size; + if (::getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, &sizeSize) != 0) { + THROW_SYSCALL("getsockopt(SO_RCVBUF)"); + } + return size; +} + + +void SocketStream::setReadBufferSize(size_t size) const +{ +#ifdef __linux__ + // Linux default and max at: /proc/sys/net/core/rmem_{default,max} + size /= 2; // Linux doubles it; getsockopt() returns the doubled value. +#endif + if (::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof size) < 0) THROW_SYSCALL("setsockopt(SO_RCVBUF)"); } diff --git a/LCS/Stream/test/tNetFuncs.cc b/LCS/Stream/test/tNetFuncs.cc index ba334cb0b6f07cf370ea5cbd633db0e919d7f7d1..89f1a27e48fa31f6abea37e9c7c8fe2176f00400 100644 --- a/LCS/Stream/test/tNetFuncs.cc +++ b/LCS/Stream/test/tNetFuncs.cc @@ -24,7 +24,7 @@ #include <lofar_config.h> //# Includes -#include "../src/NetFuncs.h" +#include <Stream/NetFuncs.h> #include <Common/LofarLogger.h> diff --git a/LCU/PPSTune/CMakeLists.txt b/LCU/PPSTune/CMakeLists.txt index a6ad8cee00d22b58eac16b76cb475978b69c0337..e70eac3a4392d43a2c914667d75ff52bdc246b86 100644 --- a/LCU/PPSTune/CMakeLists.txt +++ b/LCU/PPSTune/CMakeLists.txt @@ -2,6 +2,8 @@ lofar_package(PPSTune 1.0) +add_subdirectory(ppstune) + # Install files matching regex pattern in current directory and below install(DIRECTORY . DESTINATION sbin diff --git a/LCU/PPSTune/ppstune/CMakeLists.txt b/LCU/PPSTune/ppstune/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6533322552851dbf75eee11c4c495470f47588f0 --- /dev/null +++ b/LCU/PPSTune/ppstune/CMakeLists.txt @@ -0,0 +1,6 @@ +# $Id: CMakeLists.txt 26657 2013-09-24 11:35:59Z schoenmakers $ + +# Install files matching regex pattern in current directory and below +install(FILES + ppstune.py + DESTINATION sbin) diff --git a/LCU/checkhardware/checkHardware.conf b/LCU/checkhardware/checkHardware.conf deleted file mode 100644 index d2ddd83069cc92a929b2406d1326e6a2cc482bef..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/checkHardware.conf +++ /dev/null @@ -1,82 +0,0 @@ - -# configuration file for checkHardware.py -# -# tests to do in given levels, level-0 = empty, test can be given as arguments -# -# Select checks to do, can be combined with all levels -# -s(rcumode) : signal check for rcumode (also down and flat-check in rcumode 1..4). -# -o(rcumode) : oscillation check for rcumode. -# -sp(rcumode) : spurious check for rcumode. -# -n(rcumode)[=300] : noise check for rcumode, optional data time in seconds -# default data time = 120 sec. -# -e(rcumode)[=60] : do all RCU5 element tests, optional data time in seconds. -# default data time = 10 sec. -# -m(rcumode) : do modem check. -# -sn(rcumode) : do summator noise check. -# -# -rcu(mode) : do all rcu checks for given mode, no element tests done. -# -# -rbc : RSP voltage/temperature check -# -spu : SPU voltage check. -# -tm : TBB memmory check. -# - -level-0-tests= -level-1-tests=RBC,SPU,TM,RCU1,RCU3,RCU5 -level-2-tests=RBC,SPU,TM,RCU1,RCU3,RCU5,E5 -level-3-tests=S1,S3 - -# TBB versions -tbbdriver-version=2.51 -tbbctl-version=2.51 -tp-version=2.4 -mp-version=3.0 - -# RSP versions -ap-version=8.2 -bp-version=8.2 -#ap-version=9.3 -#bp-version=9.3 - -# LBA test settings, limits in dB -# limits (min/max) in dB -# test-sb = subband used in RF test - -lbl-test-sb=301 -lbh-test-sb=301 - -lba-rf-min-signal=75.0 -lba-rf-min-deviation=-2.0 -lba-rf-max-deviation=2.0 - -lba-noise-min-deviation=-2.5 -lba-noise-max-deviation=2.5 -lba-noise-max-difference=1.5 - - -# RCU5 test settings -# limits (min/max) in dB -# test_sb = subband used in RF test - -hba-test-sb=357 - -hba-rf-min-signal=80.0 -hba-rf-min-deviation=-24.0 -hba-rf-max-deviation=12.0 - -ehba-rf-min-signal=70.0 -ehba-rf-min-deviation=-24.0 -ehba-rf-max-deviation=12.0 - -hba-noise-min-deviation=-3.0 -hba-noise-max-deviation=1.5 -hba-noise-max-difference=1.5 - -ehba-noise-min-deviation=-3.0 -ehba-noise-max-deviation=1.5 -ehba-noise-max-difference=1.5 - -# General settings -log-dir-global=/globalhome/log/stationtest -log-dir-local=/opt/stationtest/data - diff --git a/LCU/checkhardware/checkHardware.py b/LCU/checkhardware/checkHardware.py deleted file mode 100755 index cda79248ee1393dbc6e399cb8315b95d38f9da47..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/checkHardware.py +++ /dev/null @@ -1,707 +0,0 @@ -#!/usr/bin/python - - -info = ''' ---------------------------------------------------------------------------- - Usage of arguments - -cf=fullfilename : full path and filename for the configurationfile to use. - -l=2 : set level to 2 (default level is 0) - level 0 : manual checks, use keys listed below - level 1..n : see checkhardware.conf file for checks done - - To start a long check set number of runs or start and stop time, if start time - is not given the first run is started immediately - -r=1 : repeats, number of runs to do - -start=[date_time]: start time of first run, format [YYYYMMDD_HH:MM:SS] - -stop=[date_time] : stop time of last run, format [YYYYMMDD_HH:MM:SS] - - Set logging level, can be: debug|info|warning|error - -ls=debug : print all information on screen, default=info - -lf=info : print debug|warning|error information to log file, default=debug - - Select checks to do, can be combined with all levels - -s(rcumode) : signal check for rcumode - -sh(rcumode) : short test for rcumode 1..4 - -f(rcumode) : flat test for rcumode 1..4 - -d(rcumode) : down test for rcumode 1..4 - -o(rcumode) : oscillation check for rcumode - -sp(rcumode) : spurious check for rcumode - -n(rcumode)[=120] : noise check for rcumode, optional data time in seconds default = 120 sec. - -e(rcumode)[=120] : do all HBA element tests, optional data time in seconds default = 10 sec. - -sn(rcumode) : HBA summator noise check. - -m(rcumode) : HBA modem check. - - -rcu(mode) : do all rcu(mode) tests - - -rv : RSP version check, always done - -tv : TBB version check, always done - - -rbc : RSP board check, voltage and temperature - -spu : SPU voltage check - -tm : TBB memmory check - - example : ./checkHardware.py -s5 -n5=180 - ----------------------------------------------------------------------------''' - - -import os -import sys -import traceback - -check_version = '0815' - -mainPath = r'/opt/stationtest' -libPath = os.path.join(mainPath, 'lib') -sys.path.insert(0, libPath) - -logPath = r'/localhome/stationtest/log' - -import time -import datetime -import logging - -from general_lib import * -from lofar_lib import * -from test_lib import * -from test_db import * -from data_lib import * -from search_lib import search_version - -os.umask(001) - -logger = None - -rcu_keys = ('RCU1', 'RCU2', 'RCU3', 'RCU4', 'RCU5', 'RCU6', 'RCU7') -rcu_m1_keys = ('O1', 'SP1', 'N1', 'S1', 'SH1', 'D1', 'F1') -rcu_m2_keys = ('O2', 'SP2', 'N2', 'S2', 'SH2', 'D2', 'F2') -rcu_m3_keys = ('O3', 'SP3', 'N3', 'S3', 'SH3', 'D3', 'F3') -rcu_m4_keys = ('O4', 'SP4', 'N4', 'S4', 'SH4', 'D4', 'F4') -rcu_m5_keys = ('M5', 'O5', 'SN5', 'SP5', 'N5', 'S5', 'E5') -rcu_m6_keys = ('M6', 'O6', 'SN6', 'SP6', 'N6', 'S6', 'E6') -rcu_m7_keys = ('M7', 'O7', 'SN7', 'SP7', 'N7', 'S7', 'E7') - -rcu_m12_keys = rcu_m1_keys + rcu_m2_keys -rcu_m34_keys = rcu_m3_keys + rcu_m4_keys -rcu_m567_keys = rcu_m5_keys + rcu_m6_keys + rcu_m7_keys - -rsp_keys = ('RV', 'SPU', 'RBC') + rcu_keys + rcu_m12_keys + rcu_m34_keys + rcu_m567_keys -tbb_keys = ('TV', 'TM') -control_keys = ('R', 'START', 'STOP') -all_keys = control_keys + rsp_keys + tbb_keys -rsp_check = False -tbb_check = False - -args = dict() - -# version checks are always done -args['RV'] = '-' -args['TV'] = '-' - - -def printHelp(): - print info - - -# return readable info for test -def getTestInfo(key=''): - if key[-1] in '1234567': - test_name = '' - test = key[:-1] - mode = key[-1] - - if mode in '1234': - ant_type = 'LBA' - else: - ant_type = 'HBA' - - if test in ('O',): - test_name += 'Oscillation' - if test in ('SP',): - test_name += 'Spurious' - if test in ('N',): - test_name += 'Noise' - if test in ('S',): - test_name += 'RF' - if test in ('SH',): - test_name += 'Short' - if test in ('D',): - test_name += 'Down' - if test in ('F',): - test_name += 'Flat' - if test in ('S',): - test_name += 'RF' - if test in ('SN',): - test_name += 'Summator noise' - if test in ('E',): - test_name += 'Element' - if test in ('M',): - test_name += 'Modem' - if test in ('RCU',): - test_name += 'All tests' - - return '%s mode-%c %s check' % (ant_type, mode, test_name) - - if key in 'RV': - return 'RSP Version check' - if key in 'SPU': - return 'SPU check' - if key in 'RBC': - return 'RSP board check' - if key in 'TV': - return 'TBB Version check' - if key in 'TM': - return 'TBB Memory check' - if key in 'START': - return 'START checks' - if key in 'STOP': - return 'STOP checks' - if key in 'R': - return 'Number of test repeats set to' - return('') - - -def addToArgs(key, value): - if key == '': - return - global args, rsp_check, tbb_check - if key in rsp_keys or key in tbb_keys or key in ('H', 'L', 'LS', 'LF', 'R', 'START', 'STOP'): - if value != '-': - args[key] = value - else: - args[key] = '-' - - if key in rsp_keys: - rsp_check = True - if key in tbb_keys: - tbb_check = True - else: - sys.exit('Unknown key %s' % (key)) - return - - -def getArguments(): - key = '' - value = '-' - for arg in sys.argv[1:]: - if arg[0] == '-': - opt = arg[1:].upper() - valpos = opt.find('=') - if valpos != -1: - key, value = opt.strip().split('=') - else: - key, value = opt, '-' - addToArgs(key=key, value=value) - return - - -# get checklevel and set tests to do -def setLevelTests(conf): - level = args.get('L', '0') - if level == '0': - return - tests = conf.getStr('level-%s-tests' % (level)).split(',') - for tst in tests: - opt = tst.upper() - valpos = opt.find('=') - if valpos != -1: - key, value = opt.strip().split('=') - else: - key, value = opt, '-' - key = tst.upper() - addToArgs(key=key, value=value) - return - - -# get and unpack configuration file -class cConfiguration: - def __init__(self): - self.conf = dict() - if 'CF' in args: - filename = args.get('CF') - else: - filename = '/localhome/stationtest/config/checkHardware.conf' - - f = open('/localhome/stationtest/config/checkHardware.conf', 'r') - data = f.readlines() - f.close() - for line in data: - if line[0] in ('#', '\n', ' '): - continue - if line.find('#') > 0: - line = line[:line.find('#')] - try: - key, value = line.strip().split('=') - key = key.replace('_', '-') - self.conf[key] = value - except: - print 'Not a valid configuration setting: %s' % (line) - - def getInt(self, key, default=0): - return (int(self.conf.get(key, str(default)))) - - def getFloat(self, key, default=0.0): - return (float(self.conf.get(key, str(default)))) - - def getStr(self, key): - return (self.conf.get(key, '')) - - -# setup default python logging system -# logstream for screen output -# filestream for program log file -def init_logging(): - global logger - - log_levels = {'DEBUG' : logging.DEBUG, - 'INFO' : logging.INFO, - 'WARNING': logging.WARNING, - 'ERROR' : logging.ERROR} - - try: - screen_log_level = args.get('LS', 'WARNING') - file_log_level = args.get('LF', 'DEBUG') - except: - print 'Not a legal log level, try again' - sys.exit(-1) - - station = getHostName() - - # create logger - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - - # check if log dir exist - if not os.access(logPath, os.F_OK): - os.mkdir(logPath) - - # create file handler - full_filename = os.path.join(logPath, 'checkHardware.log') - # backup_filename = os.path.join(mainPath, 'checkHardware_bk.log') - # sendCmd('cp', '%s %s' % (full_filename, backup_filename)) - file_handler = logging.FileHandler(full_filename, mode='w') - formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') - file_handler.setFormatter(formatter) - file_handler.setLevel(log_levels[file_log_level]) - logger.addHandler(file_handler) - - if len(logger.handlers) == 1: - # create console handler - stream_handler = logging.StreamHandler() - fmt = '%s %%(levelname)-8s %%(message)s' % (station) - formatter = logging.Formatter(fmt) - stream_handler.setFormatter(formatter) - stream_handler.setLevel(log_levels[screen_log_level]) - logger.addHandler(stream_handler) - return - - -def backupLogFiles(): - for nr in range(8, -1, -1): - if nr == 0: - full_filename = os.path.join(logPath, 'checkHardware.log') - else: - full_filename = os.path.join(logPath, 'checkHardware.log.%d' % (nr)) - full_filename_new = os.path.join(logPath, 'checkHardware.log.%d' % (nr+1)) - if os.path.exists(full_filename): - os.rename(full_filename, full_filename_new) - return - - -def waitForStart(start_datetime): - start_time = time.mktime(start_datetime.timetuple()) - if start_time > time.time(): - logger.info('delayed start, sleep till %s' % (time.asctime(start_datetime.timetuple()))) - - while start_time > time.time(): - wait_time = start_time - time.time() - sleep_time = min(wait_time, 3600.0) - time.sleep(sleep_time) - return - - -def main(): - getArguments() - # print args - if len(args) == 0 or 'H' in args: - printHelp() - sys.exit() - - backupLogFiles() # backup logfiles, max 10 logfiles .9 is the oldest - - init_logging() - init_lofar_lib() - init_test_db() - init_test_lib() - init_data_lib() - - conf = cConfiguration() - - setLevelTests(conf) - - StID = getHostName() - - logger.info('== START HARDWARE CHECK ==') - logger.info('== requested checks and settings ==') - logger.info('-'*40) - for i in all_keys: - if i in args: - if args.get(i) == '-': - logger.info(' %s' % (getTestInfo(i))) - else: - logger.info(' %s, time = %s' % (getTestInfo(i), args.get(i))) - logger.info('-'*40) - - removeAllDataFiles() - - # use format YYYYMMDD_HH:MM:SS - stop_time = -1 - if 'STOP' in args: - stop = args.get('STOP') - if len(stop) != 17: - return 'wrong stoptime format must be YYYYMMDD_HH:MM:SS' - stop_datetime = datetime.datetime(int(stop[:4]), int(stop[4:6]), int(stop[6:8]), - int(stop[9:11]), int(stop[12:14]), int(stop[15:])) - stop_time = time.mktime(stop_datetime.timetuple()) - - if 'START' in args: - start = args.get('START') - if len(start) != 17: - return 'wrong starttime format must be YYYYMMDD_HH:MM:SS' - start_datetime = datetime.datetime(int(start[:4]), int(start[4:6]), int(start[6:8]), - int(start[9:11]), int(start[12:14]), int(start[15:])) - if (time.mktime(start_datetime.timetuple()) < time.time()): - # print time.mktime(start_datetime.timetuple()), time.time() - logger.error('Stop program, StartTime in past') - return 2 - if(time.mktime(start_datetime.timetuple()) > stop_time): - logger.error('Stop program, stop before start') - return 2 - waitForStart(start_datetime) - - logger.info('run checks till %s' % (time.asctime(stop_datetime.timetuple()))) - - start_time = time.gmtime() - # Read in RemoteStation.conf - ID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT = readStationConfig() - - # setup intern database with station layout - db = cDB(StID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT) - - if (stop_time > 0.0): - db.setTestEndTime((stop_time-120.0)) - - # set manualy marked bad antennas - global_log_dir = conf.getStr('log-dir-global') - host = getHostName() - if os.path.exists(global_log_dir): - full_filename = os.path.join(global_log_dir, 'bad_antenna_list.txt') - logger.info('add bad_antenna_list data from file "%s" to db' % (full_filename)) - f = open(full_filename, 'r') - data = f.readlines() - for line in data: - if line[0] == '#': - continue - ant_list = line.strip().split(' ') - if ant_list[0].strip().upper() == host.upper(): - if len(ant_list) > 1: - for ant in ant_list[1:]: - ant_type = ant[:3].strip().upper() - if ant_type == 'LBA': - ant_nr = int(ant[3:].strip()) - # print 'ant type=%s nr=%d' % (ant_type, ant_nr) - if ant_nr < nLBH: - db.lbh.ant[ant_nr].on_bad_list = 1 - else: - db.lbl.ant[ant_nr-nLBH].on_bad_list = 1 - elif ant_type == 'HBA': - ant_nr = int(ant[3:].strip()) - # print 'ant type=%s nr=%d' % (ant_type, ant_nr) - db.hba.tile[ant_nr].on_bad_list = 1 - break - else: - logger.warn('bad_antenna_list data from file "%s" not found' % (full_filename)) - - db.script_versions = 'CHECK=%s,DB=%s,TEST=%s,SEARCH=%s,LOFAR=%s,GENERAL=%s' %\ - (check_version, db_version, test_version, search_version, lofar_version, general_version) - db.check_start_time = time.gmtime() - - writeMessage('!!! This station will be in use for a test! Please do not use the station! (script version %s) !!!' % (check_version)) - start_level, board_errors = swlevel() - sw_level, board_errors = swlevel(2) - if start_level == 1: - logger.info('Wait 30 seconds while startup RSPDriver') - time.sleep(30.0) - if sw_level == 2: - tbb = cTBB(db) - rsp = cRSP(db) - spu = cSPU(db) - - # do RSP tests if requested - if rsp_check is True: - # check if RSPDriver is running - if checkActiveRSPDriver() == False: - logger.warn('RSPDriver not running') - else: - # wait for RSP boards ready, and reset 48V if needed, max 2x if no board errors after 48V reset - rsp_ready = False - restarts = 2 - while (not rsp_ready) and (restarts > 0): - if waitRSPready() == False: - logger.warn('Not all RSP boards ready, reset 48V to recover') - swlevel(1) - reset48V() - restarts -= 1 - time.sleep(30.0) - level, board_errors = swlevel(2) - if len(board_errors) > 0: - db.board_errors = board_errors - restarts = 0 - else: - time.sleep(30.0) - else: - rsp_ready = True - restarts = 2 - - # if all rsp boards ready do all tests - if rsp_ready: - if 'RV' in args: - rsp.checkVersions(conf.getStr('bp-version'), conf.getStr('ap-version')) - - resetRSPsettings() - - lbl = cLBA(db, db.lbl) - lbh = cLBA(db, db.lbh) - hba = cHBA(db, db.hba) - - repeats = int(args.get('R', '1')) - repeat_cnt = 1 - - runtime = 0 - db.tests = '' - while (repeat_cnt <= repeats) or ((stop_time > -1) and ((time.time() + runtime) < stop_time)): - - try: - runstart = time.time() - if stop_time > -1: - logger.info('\n=== Start testrun %d ===\n' % (repeat_cnt)) - else: - logger.info('\n=== Start testrun %d of %d ===\n' % (repeat_cnt, repeats)) - - if 'SPU' in args: - spu.checkStatus() - - if 'RBC' in args: - rsp.checkBoard() - - # check if mode 1,2 is available on this station - if StID in CoreStations or StID in RemoteStations: - for mode in (1, 2): - lbl.reset() - # do all rcumode 1,2 tests - if 'RCU%d' % mode in args or 'SH%d' % mode in args: - lbl.checkShort(mode=mode) - - if 'RCU%d' % mode in args or 'F%d' % mode in args: - lbl.checkFlat(mode=mode) - - if 'RCU%d' % mode in args or 'D%d' % mode in args: - lbl.checkDown(mode=mode, subband=conf.getInt('lbl-test-sb', 301)) - - if 'RCU%d' % mode in args or 'O%d' % mode in args: - lbl.checkOscillation(mode=mode) - - if 'RCU%d' % mode in args or 'SP%d' % mode in args: - lbl.checkSpurious(mode=mode) - - if 'RCU%d' % mode in args or 'N%d' % mode in args: - if 'RCU%d' % mode in args or args.get('N%d' % (mode)) == '-': - recordtime = 120 - else: - recordtime = int(args.get('N%d' % (mode))) - lbl.checkNoise( - mode=mode, - record_time=recordtime, - low_deviation=conf.getFloat('lba-noise-min-deviation', -3.0), - high_deviation=conf.getFloat('lba-noise-max-deviation', 2.5), - max_diff=conf.getFloat('lba-noise-max-difference', 2.0)) - - if 'RCU%d' % mode in args or 'S%d' % mode in args: - lbl.checkSignal( - mode=mode, - subband=conf.getInt('lbl-test-sb', 301), - min_signal=conf.getFloat('lba-rf-min-signal', 75.0), - low_deviation=conf.getFloat('lba-rf-min-deviation', -2.0), - high_deviation=conf.getFloat('lba-rf-max-deviation', 2.0)) - - for mode in (3, 4): - lbh.reset() - # do all rcumode 3,4 tests - if 'RCU%d' % mode in args or 'SH%d' % mode in args: - lbh.checkShort(mode=mode) - - if 'RCU%d' % mode in args or 'F%d' % mode in args: - lbh.checkFlat(mode=mode) - - if 'RCU%d' % mode in args or 'D%d' % mode in args: - lbh.checkDown(mode=mode, subband=conf.getInt('lbh-test-sb', 301)) - - if 'RCU%d' % mode in args or 'O%d' % mode in args: - lbh.checkOscillation(mode=mode) - - if 'RCU%d' % mode in args or 'SP%d' % mode in args: - lbh.checkSpurious(mode=mode) - - if 'RCU%d' % mode in args or 'N%d' % mode in args: - if 'RCU%d' % mode in args or args.get('N%d' % (mode)) == '-': - recordtime = 120 - else: - recordtime = int(args.get('N%d' % (mode))) - lbh.checkNoise( - mode=mode, - record_time=recordtime, - low_deviation=conf.getFloat('lba-noise-min-deviation', -3.0), - high_deviation=conf.getFloat('lba-noise-max-deviation', 2.5), - max_diff=conf.getFloat('lba-noise-max-difference', 1.5)) - - if 'RCU%d' % mode in args or 'S%d' % mode in args: - lbh.checkSignal( - mode=mode, - subband=conf.getInt('lbh-test-sb', 301), - min_signal=conf.getFloat('lba-rf-min-signal', 75.0), - low_deviation=conf.getFloat('lba-rf-min-deviation', -2.0), - high_deviation=conf.getFloat('lba-rf-max-deviation', 2.0)) - - for mode in (5, 6, 7): - # do all rcumode 5, 6, 7 tests - hba.reset() - - if 'RCU%d' % mode in args or 'M%d' % mode in args: - hba.checkModem(mode=mode) - hba.turnOffBadTiles() - - if 'RCU%d' % mode in args or 'O%d' % mode in args: - hba.checkOscillation(mode=mode) - - if 'RCU%d' % mode in args or 'SN%d' % mode in args: - hba.checkSummatorNoise(mode=mode) - - if 'RCU%d' % mode in args or 'SP%d' % mode in args: - hba.checkSpurious(mode=mode) - - if 'RCU%d' % mode in args or 'N%d' % mode in args: - if 'RCU%d' % mode in args or args.get('N%d' % (mode)) == '-': - recordtime = 120 - else: - recordtime = int(args.get('N%d' % (mode))) - hba.checkNoise( - mode=mode, - record_time=recordtime, - low_deviation=conf.getFloat('hba-noise-min-deviation', -3.0), - high_deviation=conf.getFloat('hba-noise-max-deviation', 2.5), - max_diff=conf.getFloat('hba-noise-max-difference', 2.0)) - - # if 'RCU%d' % mode in args or 'S%d' % mode in args: - if 'S%d' % mode in args: - hba.checkSignal( - mode=mode, - subband=conf.getInt('hba-test-sb', 155), - min_signal=conf.getFloat('hba-rf-min-signal', 80.0), - low_deviation=conf.getFloat('hba-rf-min-deviation', -24.0), - high_deviation=conf.getFloat('hba-rf-max-deviation', 12.0)) - - runtime = (time.time() - runstart) - - # All element test - if 'E%d' % mode in args: - if args.get('E%d' % (mode)) == '-': - recordtime = 10 - else: - recordtime = int(args.get('E%d' % (mode))) - - hba.checkElements( - mode=mode, - record_time=recordtime, - subband=conf.getInt('hba-test-sb', 155), - noise_low_deviation=conf.getFloat('ehba-noise-min-deviation', -3.0), - noise_high_deviation=conf.getFloat('ehba-noise-max-deviation', 2.5), - noise_max_diff=conf.getFloat('ehba-noise-max-difference', 1.5), - rf_min_signal=conf.getFloat('ehba-rf-min-signal', 70.0), - rf_low_deviation=conf.getFloat('ehba-rf-min-deviation', -24.0), - rf_high_deviation=conf.getFloat('ehba-rf-max-deviation', 12.0)) - - # stop test if driver stopped - db.rsp_driver_down = not checkActiveRSPDriver() - if db.rsp_driver_down and (restarts > 0): - restarts -= 1 - reset48V() - time.sleep(30.0) - level, board_errors = swlevel(2) - if len(board_errors) > 0: - db.board_errors = board_errors - break - else: - time.sleep(30.0) - - # one run done - repeat_cnt += 1 - - except: - logging.error('Caught %s', str(sys.exc_info()[0])) - logging.error(str(sys.exc_info()[1])) - logging.error('TRACEBACK:\n%s', traceback.format_exc()) - logging.error('Aborting NOW') - break - - db.rsp_driver_down = not checkActiveRSPDriver() - if not db.rsp_driver_down: - resetRSPsettings() - - # do TBB tests if requested - db.tbb_driver_down = not checkActiveTBBDriver() - if (not db.tbb_driver_down) and (tbb_check is True): - # wait for TBB boards ready - if waitTBBready(nTBB) == 1: - try: - if 'TV' in args: - db.addTestDone('TV') - tbb.checkVersions(conf.getStr('tbbdriver-version'), conf.getStr('tbbctl-version'), - conf.getStr('tp-version'), conf.getStr('mp-version')) - if 'TM' in args: - db.addTestDone('TM') - tbb.checkMemory() - except: - logging.error('Program fault, TBB test') - logging.error('Caught %s', str(sys.exc_info()[0])) - logging.error(str(sys.exc_info()[1])) - logging.error('TRACEBACK:\n%s', traceback.format_exc()) - logging.error('Aborting NOW') - - db.check_stop_time = time.gmtime() - - try: - # do db test and write result files to log directory - log_dir = conf.getStr('log-dir-local') - if os.path.exists(log_dir): - logger.info('write result data') - db.test(log_dir) - else: - logger.warn('not a valid log directory') - if not db.rsp_driver_down: - logger.info('Going back to swlevel %d' % (start_level)) - swlevel(start_level) - # delete files from data directory - removeAllDataFiles() - except: - logging.error('Program fault, reporting and cleanup') - logging.error('Caught %s', str(sys.exc_info()[0])) - logging.error(str(sys.exc_info()[1])) - logging.error('TRACEBACK:\n%s', traceback.format_exc()) - logging.error('Aborting NOW') - - logger.info('Test ready.') - writeMessage('!!! The test is ready and the station can be used again! !!!') - - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/LCU/checkhardware/check_hardware.py b/LCU/checkhardware/check_hardware.py new file mode 100755 index 0000000000000000000000000000000000000000..a267b3a546522e00422954471127c5452188f0b8 --- /dev/null +++ b/LCU/checkhardware/check_hardware.py @@ -0,0 +1,639 @@ +#!/usr/bin/python + + +info = ''' ---------------------------------------------------------------------------- + Usage of arguments + -cf=fullfilename : full path and filename for the configurationfile to use. + -l=2 : set level to 2 (default level is 0) + level 0 : manual checks, use keys listed below + level 1..n : see checkhardware.conf file for checks done + + To start a long check set number of runs or start and stop time, if start time + is not given the first run is started immediately + -r=1 : repeats, number of runs to do + -start=[date_time]: start time of first run, format [YYYYMMDD_HH:MM:SS] + -stop=[date_time] : stop time of last run, format [YYYYMMDD_HH:MM:SS] + + Set logging level, can be: debug|info|warning|error + -ls=debug : print all information on screen, default=info + -lf=info : print debug|warning|error information to log file, default=debug + + Select checks to do, can be combined with all levels + -s(rcumode) : signal check for rcumode + -sh(rcumode) : short test for rcumode 1..4 + -f(rcumode) : flat test for rcumode 1..4 + -d(rcumode) : down test for rcumode 1..4 + -o(rcumode) : oscillation check for rcumode + -sp(rcumode) : spurious check for rcumode + -n(rcumode)[=120] : noise check for rcumode, optional data time in seconds default = 120 sec. + -e(rcumode)[=12] : do all HBA element tests, optional data time in seconds default = 4 sec. + -sn(rcumode) : HBA summator noise check. + -m(rcumode) : HBA modem check. + + -rcu(mode) : do all rcu(mode) tests + + -rv : RSP version check, always done + -tv : TBB version check, always done + + -rbc : RSP board check, voltage and temperature + -tbc : TBB board check, voltage and temperature + -spu : SPU voltage check + -tm : TBB memmory check + + example : ./checkHardware.py -s5 -n5=180 + ----------------------------------------------------------------------------''' + + +import os +import sys +import traceback +from time import sleep +import datetime +from socket import gethostname +import logging + +os.umask(001) + +conf_file = r'check_hardware.conf' + +mainpath = r'/opt/stationtest' +maindatapath = r'/localhome/stationtest' + +confpath = os.path.join(maindatapath, 'config') +logpath = os.path.join(maindatapath, 'log') + +# if not exists make path +if not os.access(logpath, os.F_OK): + os.mkdir(logpath) + +hostname = gethostname().split('.')[0].upper() +station_name = hostname + +# first start main logging before including checkhardware_lib +# backup log files +for nr in xrange(8, -1, -1): + if nr == 0: + full_filename = os.path.join(logpath, 'check_hardware.log') + else: + full_filename = os.path.join(logpath, 'check_hardware.log.%d' % nr) + full_filename_new = os.path.join(logpath, 'check_hardware.log.%d' % (nr + 1)) + if os.path.exists(full_filename): + os.rename(full_filename, full_filename_new) + +# make and setup logger +logger = logging.getLogger('main') +logger.setLevel(logging.DEBUG) +# create file handler +filename = 'check_hardware.log' +full_filename = os.path.join(logpath, filename) +file_handler = logging.FileHandler(full_filename, mode='w') +formatter = logging.Formatter('%(asctime)s %(name)-14s %(levelname)-8s %(message)s') +file_handler.setFormatter(formatter) +file_handler.setLevel(logging.DEBUG) +logger.addHandler(file_handler) + +# create console handler +stream_handler = logging.StreamHandler() +formatter = logging.Formatter('%(asctime)s %(name)-14s %(levelname)-7s %(message)s') +stream_handler.setFormatter(formatter) +stream_handler.setLevel(logging.WARNING) +logger.addHandler(stream_handler) + +file_logger_handler = logger.handlers[0] +screen_logger_handler = logger.handlers[1] + +sleep(2.0) +logger.debug("logger is working") + +# now include checkhardware library +from checkhardware_lib import * + +check_version = '0516' + +rcu_keys = ('RCU1', 'RCU2', 'RCU3', 'RCU4', 'RCU5', 'RCU6', 'RCU7') +rcu_m1_keys = ('O1', 'SP1', 'N1', 'S1', 'SH1', 'D1', 'F1') +rcu_m2_keys = ('O2', 'SP2', 'N2', 'S2', 'SH2', 'D2', 'F2') +rcu_m3_keys = ('O3', 'SP3', 'N3', 'S3', 'SH3', 'D3', 'F3') +rcu_m4_keys = ('O4', 'SP4', 'N4', 'S4', 'SH4', 'D4', 'F4') +rcu_m5_keys = ('M5', 'O5', 'SN5', 'SP5', 'N5', 'S5', 'E5') +rcu_m6_keys = ('M6', 'O6', 'SN6', 'SP6', 'N6', 'S6', 'E6') +rcu_m7_keys = ('M7', 'O7', 'SN7', 'SP7', 'N7', 'S7', 'E7') + +rcu_m12_keys = rcu_m1_keys + rcu_m2_keys +rcu_m34_keys = rcu_m3_keys + rcu_m4_keys +rcu_m567_keys = rcu_m5_keys + rcu_m6_keys + rcu_m7_keys + +rsp_keys = ('RV', 'SPU', 'RBC') + rcu_keys + rcu_m12_keys + rcu_m34_keys + rcu_m567_keys +tbb_keys = ('TV', 'TM', 'TBC') +control_keys = ('R', 'START', 'STOP', 'TST') +all_keys = control_keys + rsp_keys + tbb_keys +rsp_check = False +tbb_check = False + +args = dict() + +# version checks are always done +args['RV'] = '-' +args['TV'] = '-' + +def print_help(): + print info + + +# return readable info for test +def get_test_info(key=''): + if key[-1] in '1234567': + test_name = '' + test = key[:-1] + mode = key[-1] + + if mode in '1234': + ant_type = 'LBA' + else: + ant_type = 'HBA' + + if test in ('O',): + test_name += 'Oscillation' + if test in ('SP',): + test_name += 'Spurious' + if test in ('N',): + test_name += 'Noise' + if test in ('S',): + test_name += 'RF' + if test in ('SH',): + test_name += 'Short' + if test in ('D',): + test_name += 'Down' + if test in ('F',): + test_name += 'Flat' + if test in ('SN',): + test_name += 'Summator noise' + if test in ('E',): + test_name += 'Element' + if test in ('M',): + test_name += 'Modem' + if test in ('RCU',): + test_name += 'All tests' + + return '%s mode-%c %s check' % (ant_type, mode, test_name) + + if key == 'RV': + return 'RSP Version check' + if key == 'SPU': + return 'SPU check' + if key == 'RBC': + return 'RSP board checks' + if key == 'TV': + return 'TBB Version check' + if key == 'TM': + return 'TBB Memory check' + if key == 'TBC': + return 'TBB board checks' + if key == 'START': + return 'START checks' + if key == 'STOP': + return 'STOP checks' + if key == 'R': + return 'Number of test repeats set to' + return '' + + +def add_to_args(key, value): + if key == '': + return + global args, rsp_check, tbb_check + if key in rsp_keys or key in tbb_keys or key in ('H', 'L', 'LS', 'LF', 'R', 'START', 'STOP', 'TST'): + if value != '-': + args[key] = value + else: + args[key] = '-' + + if key in rsp_keys: + rsp_check = True + if key in tbb_keys: + tbb_check = True + else: + sys.exit('Unknown key %s' % key) + return + + +def get_arguments(): + for arg in sys.argv[1:]: + if arg[0] == '-': + opt = arg[1:].strip().upper() + valpos = opt.find('=') + if valpos != -1: + key, value = opt.split('=') + else: + key, value = opt, '-' + add_to_args(key=key.strip(), value=value.strip()) + return + + +# get checklevel and set tests to do +def set_tests(conf): + level = args.get('L', '0') + if level == '0': + return + tests = conf.as_string('always').split(',') + tests += conf.as_string('list.%s' % level).split(',') + logger.debug("test= %s" % tests) + for tst in tests: + opt = tst.strip().upper() + valpos = opt.find('=') + if valpos != -1: + key, value = opt.split('=') + else: + key, value = opt, '-' + add_to_args(key=key.strip(), value=value.strip()) + return + +# setup default python logging system +# logstream for screen output +def init_logging(): + log_levels = {'DEBUG': logging.DEBUG, + 'INFO': logging.INFO, + 'WARNING': logging.WARNING, + 'ERROR': logging.ERROR} + + file_log_level = args.get('LF', 'DEBUG') + if file_log_level not in log_levels: + sys.exit('LF=%s, Not a legal log level, try again' % file_log_level) + + screen_log_level = args.get('LS', 'WARNING') + if screen_log_level not in log_levels: + sys.exit('LS=%s, not a legal log level, try again' % screen_log_level) + + if 'LF' in args: + file_logger_handler.setLevel(log_levels[file_log_level]) + if 'LS' in args: + screen_logger_handler.setLevel(log_levels[screen_log_level]) + return + + +def wait_for_start(start_datetime): + start_time = time.mktime(start_datetime.timetuple()) + if start_time > time.time(): + logger.info('delayed start, sleep till %s' % (time.asctime(start_datetime.timetuple()))) + + while start_time > time.time(): + wait_time = start_time - time.time() + sleep_time = min(wait_time, 3600.0) + time.sleep(sleep_time) + return + + +def main(): + global station_name + get_arguments() + # print args + if len(args) == 0 or 'H' in args: + print_help() + sys.exit() + + init_logging() + init_lofar_lib() + + full_filename = os.path.join(confpath, conf_file) + conf = TestSettings(filename=full_filename) + + if 'TST' in args: + lofar.testmode = True + logger.info("**** NOW IN TESTMODE ****") + + set_tests(conf.group('check')) + + logger.info('== START HARDWARE CHECK ==') + logger.info('== requested checks and settings ==') + logger.info('-'*40) + for i in all_keys: + if i in args: + if args.get(i) == '-': + logger.info(' %s' % (get_test_info(i))) + else: + logger.info(' %s, time = %s' % (get_test_info(i), args.get(i))) + logger.info('-'*40) + + # use format YYYYMMDD_HH:MM:SS + stop_time = -1 + if 'STOP' in args: + stop = args.get('STOP') + if len(stop) != 17: + return 'wrong stoptime format must be YYYYMMDD_HH:MM:SS' + stop_datetime = datetime.datetime(int(stop[:4]), int(stop[4:6]), int(stop[6:8]), + int(stop[9:11]), int(stop[12:14]), int(stop[15:])) + stop_time = time.mktime(stop_datetime.timetuple()) + + if 'START' in args: + start = args.get('START') + if len(start) != 17: + return 'wrong starttime format must be YYYYMMDD_HH:MM:SS' + start_datetime = datetime.datetime(int(start[:4]), int(start[4:6]), int(start[6:8]), + int(start[9:11]), int(start[12:14]), int(start[15:])) + if time.mktime(start_datetime.timetuple()) < time.time(): + # print time.mktime(start_datetime.timetuple()), time.time() + logger.error('Stop program, StartTime in past') + return 2 + if time.mktime(start_datetime.timetuple()) > stop_time: + logger.error('Stop program, stop before start') + return 2 + wait_for_start(start_datetime) + + logger.info('run checks till %s' % (time.asctime(stop_datetime.timetuple()))) + + + # Read in RemoteStation.conf + st_id, n_rsp, n_tbb, n_lbl, n_lbh, n_hba, hba_split = read_station_config() + logger.info("Station configuration %s:" % station_name) + logger.info(" ID = %d" % st_id) + logger.info(" nr RSP boards = %d" % n_rsp) + logger.info(" nr TB boards = %d" % n_tbb) + logger.info(" nr LBA low antennas = %d" % n_lbl) + logger.info(" nr LBA high antennas = %d" % n_lbh) + logger.info(" nr HBA high antennas = %d" % n_hba) + logger.info(" has HBA splitter = %d" % hba_split) + + # setup intern database with station layout + db = DB(station_name, n_rsp, n_tbb, n_lbl, n_lbh, n_hba, hba_split) + # if in local testmode + logger.debug("testmode= %s" % str(is_test_mode_active())) + if st_id == 9999: + station_name = 'CS001C' + activate_test_mode() + logger.debug("testmode= %s" % str(is_test_mode_active())) + if stop_time > 0.0: + db.set_test_end_time((stop_time - 120.0)) + + # set manualy marked bad antennas + full_filename = conf().get('files.bad-antenna-list') + try: + f = open(full_filename, 'r') + data = f.readlines() + f.close() + logger.info('get bad_antenna_list data "%s"' % full_filename) + for line in data: + if line[0] == '#': + continue + ant_list = line.strip().split(' ') + if ant_list[0].strip().upper() == station_name.upper(): + if len(ant_list) > 1: + for ant in ant_list[1:]: + ant_type = ant[:3].strip().upper() + if ant_type == 'LBA': + ant_nr = int(ant[3:].strip()) + # print 'ant type=%s nr=%d' % (ant_type, ant_nr) + if ant_nr < n_lbh: + db.lbh.ant[ant_nr].on_bad_list = 1 + else: + db.lbl.ant[ant_nr-n_lbh].on_bad_list = 1 + elif ant_type == 'HBA': + ant_nr = int(ant[3:].strip()) + # print 'ant type=%s nr=%d' % (ant_type, ant_nr) + db.hba.tile[ant_nr].on_bad_list = 1 + break + except IOError: + logger.warning('bad_antenna_list data from file "%s" not found' % full_filename) + + db.check_start_time = time.gmtime() + + write_message( + '!!! This station will be in use for a test! Please do not use the station! (script version %s) !!!' + % check_version + ) + start_level, board_errors = swlevel() + if start_level < 0: + sw_level, board_errors = swlevel(1) + start_level = abs(start_level) + sw_level, board_errors = swlevel(2) + if start_level < 2: + logger.info('Wait 30 seconds while startup RSPDriver') + time.sleep(30.0) + rsp_ready, tbb_ready = check_active_boards(db, n_rsp, n_tbb, 2) + if rsp_ready: + # do RSP tests if requested + if rsp_check is True: + if 'RV' in args: + rsp = RSP(db) + rsp.check_versions(conf.group('rsp')) + + reset_rsp_settings() + + repeats = int(args.get('R', '1')) + repeat_cnt = 1 + + runtime = 0 + db.tests = '' + while repeat_cnt <= repeats or (stop_time > -1 and (time.time() + runtime) < stop_time): + + try: + runstart = time.time() + if stop_time > -1: + logger.info('\n=== Start testrun %d ===\n' % repeat_cnt) + else: + logger.info('\n=== Start testrun %d of %d ===\n' % (repeat_cnt, repeats)) + + if 'SPU' in args: + spu = SPU(db) + spu.check_status(conf.group('spu')) + + if 'RBC' in args: + rsp = RSP(db) + rsp.check_board(conf.group('rsp')) + + # check if mode 1,2 is available on this station + if station_name in CoreStations or station_name in RemoteStations: + for mode in (1, 2): + lbl = LBA(db, db.lbl) + settings = conf.rcumode(mode) + # do all rcumode 1,2 tests + if 'RCU%d' % mode in args or 'SH%d' % mode in args: + lbl.check_short(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'F%d' % mode in args: + lbl.check_flat(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'D%d' % mode in args: + lbl.check_down(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'O%d' % mode in args: + lbl.check_oscillation(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'SP%d' % mode in args: + lbl.check_spurious(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'N%d' % mode in args: + if 'RCU%d' % mode in args or args.get('N%d' % mode) == '-': + recordtime = 60 + else: + recordtime = int(args.get('N%d' % mode)) + lbl.check_noise(mode=mode, record_time=recordtime, parset=settings) + + if 'RCU%d' % mode in args or 'S%d' % mode in args: + lbl.check_rf_power(mode=mode, parset=settings) + + for mode in (3, 4): + lbh = LBA(db, db.lbh) + settings = conf.rcumode(mode) + # do all rcumode 3,4 tests + if 'RCU%d' % mode in args or 'SH%d' % mode in args: + lbh.check_short(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'F%d' % mode in args: + lbh.check_flat(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'D%d' % mode in args: + lbh.check_down(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'O%d' % mode in args: + lbh.check_oscillation(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'SP%d' % mode in args: + lbh.check_spurious(mode=mode, parset=settings) + + if 'RCU%d' % mode in args or 'N%d' % mode in args: + if 'RCU%d' % mode in args or args.get('N%d' % mode) == '-': + recordtime = 60 + else: + recordtime = int(args.get('N%d' % mode)) + lbh.check_noise(mode=mode, record_time=recordtime, parset=settings) + + if 'RCU%d' % mode in args or 'S%d' % mode in args: + lbh.check_rf_power(mode=mode, parset=settings) + + for mode in (5, 6, 7): + # do all rcumode 5, 6, 7 tests + hba = HBA(db, db.hba) + tile_settings = conf.group('rcumode.%d.tile' % mode) + elem_settings = conf.group('rcumode.%d.element' % mode) + + if 'RCU%d' % mode in args or 'M%d' % mode in args: + hba.check_modem(mode=mode) + hba.turn_off_bad_tiles() + + if 'RCU%d' % mode in args or 'O%d' % mode in args: + hba.check_oscillation(mode=mode, parset=tile_settings) + + if 'RCU%d' % mode in args or 'SN%d' % mode in args: + hba.check_summator_noise(mode=mode, parset=tile_settings) + + if 'RCU%d' % mode in args or 'SP%d' % mode in args: + hba.check_spurious(mode=mode, parset=tile_settings) + + if 'RCU%d' % mode in args or 'N%d' % mode in args: + if 'RCU%d' % mode in args or args.get('N%d' % mode) == '-': + recordtime = 60 + else: + recordtime = int(args.get('N%d' % mode)) + hba.check_noise(mode=mode, record_time=recordtime, parset=tile_settings) + + # if 'RCU%d' % mode in args or 'S%d' % mode in args: + if 'S%d' % mode in args: + hba.check_rf_power(mode=mode, parset=tile_settings) + + runtime = (time.time() - runstart) + + # All element test + if 'E%d' % mode in args: + if args.get('E%d' % mode) == '-': + recordtime = 4 + else: + recordtime = int(args.get('E%d' % mode)) + + hba.check_elements(mode=mode, record_time=recordtime, parset=elem_settings) + + # stop test if driver stopped + db.rsp_driver_down = not check_active_rspdriver() + if db.rsp_driver_down and (restarts > 0): + restarts -= 1 + reset_48_volt() + time.sleep(30.0) + level, board_errors = swlevel(2) + if len(board_errors) > 0: + db.board_errors = board_errors + break + else: + time.sleep(30.0) + + # one run done + repeat_cnt += 1 + + except: + logger.error('Caught %s', str(sys.exc_info()[0])) + logger.error(str(sys.exc_info()[1])) + logger.error('TRACEBACK:\n%s', traceback.format_exc()) + logger.error('Aborting NOW') + break + + db.rsp_driver_down = not check_active_rspdriver() + if not db.rsp_driver_down: + reset_rsp_settings() + + + # do TBB tests if requested + if tbb_check is True: + tbb = TBB(db) + try: + if 'TV' in args: + tbb.check_versions(conf.group('tbb')) + + if 'TBC' in args: + tbb.check_board(conf.group('tbb')) + + if 'TM' in args: + tbb.check_memory() + except: + logger.error('Program fault, TBB test') + logger.error('Caught %s', str(sys.exc_info()[0])) + logger.error(str(sys.exc_info()[1])) + logger.error('TRACEBACK:\n%s', traceback.format_exc()) + logger.error('Aborting NOW') + + db.tbb_driver_down = not check_active_tbbdriver() + + db.check_stop_time = time.gmtime() + + + if len(sys.argv) > 1: + try: + # do db test and write result files to log directory + report_dir = conf().as_string('paths.local-report-dir') + if os.path.exists(report_dir): + logger.info('write result data') + db.test() + make_report(db, report_dir) + else: + logger.warning('not a valid report directory') + # delete files from data directory + remove_all_data_files() + except: + logger.error('Program fault, reporting and cleanup') + logger.error('Caught %s', str(sys.exc_info()[0])) + logger.error(str(sys.exc_info()[1])) + logger.error('TRACEBACK:\n%s', traceback.format_exc()) + logger.error('Aborting NOW') + + logger.info('Check if boards are still ok') + check_active_boards(db, n_rsp, n_tbb, 1) + + if check_active_tbbdriver() == True: + # set mode to transient (also needed to set the right ethernet header to cep), + # free all memory allocations, reallocate again and start recording + tbbctl('--mode=transient') + tbbctl('--free') + tbbctl('--alloc') + tbbctl('--record') + + if check_active_rspdriver() == True: + # activate datastream from rsp to tbb in transient mode + rspctl('--tbbmode=transient') + logger.info('Going back to swlevel %d' % start_level) + swlevel(start_level) + + logger.info('Test ready.') + write_message('!!! The test is ready and the station can be used again! !!!') + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/LCU/checkhardware/checkhardware_lib/__init__.py b/LCU/checkhardware/checkhardware_lib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..837db9972b7db5382bdac83aedf7ae111f0a03dc --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/__init__.py @@ -0,0 +1,13 @@ +#import logging +#logger = logging.getLogger('main') + +from general import * +from lofar import * +from settings import TestSettings +from db import DB, db_version +from reporting import make_report +from spu import SPU +from tbb import TBB +from rsp import RSP +from lba import LBA +from hba import HBA diff --git a/LCU/checkhardware/checkhardware_lib/data.py b/LCU/checkhardware/checkhardware_lib/data.py new file mode 100644 index 0000000000000000000000000000000000000000..83d48a9b8163cd7977a73e1ddd075f91fe9f48f0 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/data.py @@ -0,0 +1,405 @@ +#!/usr/bin/python + +""" +data library for reading in sample data +""" + +# from general_lib import * +from lofar import mode_to_band, is_test_mode_active, rspctl, select_str +import os +import numpy as np +import logging +from time import sleep + +test_version = '0815' + +logger = logging.getLogger('main.data') +logger.debug("starting data logger") + + +class AntennaData: + bands = {'10_90' : (1, 3), + '30_90' : (2, 4), + '110_190': (5,), + '170_210': (6,), + '210_250': (7,)} + + XPOL = 'x' + YPOL = 'y' + XYPOL = 'xy' + + def __init__(self, args): + self._args = args + if 'data-dir' not in args or 'n_rcus' not in args: + logger.error("missing arguments") + return + self._data_dir = self._args['data-dir'] + self._n_rcus = int(self._args.get('n_rcus', 96)) + self._sbdata = np.zeros((self._n_rcus, 1, 512), dtype=np.float64) + self._rcu_state = [0] * self._n_rcus # 0=Off, 1=On + self._rcu_mode = [0] * self._n_rcus # mode=0..7, 0=off + self._rcu_mask = [] + self._sb_mask = {} + self._rcus = {} + self._requested_seconds = 0 + for band in self.bands.keys(): + self._sb_mask[band] = [] + self._rcus[band] = {self.XPOL: [], self.YPOL: [], self.XYPOL: []} + + def _reset(self): + self._n_rcus = int(self._args.get('n_rcus', 96)) + self._rcu_state = [0] * self._n_rcus # 0=Off, 1=On + self._rcu_mode = [0] * self._n_rcus # mode=0..7, 0=off + self._rcu_mask = [] + self._sb_mask = {} + self._rcus = {} + for band in self.bands.keys(): + self._sb_mask[band] = [] + self._rcus[band] = {self.XPOL: [], self.YPOL: [], self.XYPOL: []} + + def seconds(self): + return self._sbdata.shape[1] + + def max_rcus(self): + return self._n_rcus + + def antenna(self, rcu): + ant = rcu / 2 + if self._rcu_mode[rcu] in (1, 2): + ant += 48 + return ant + + def mode(self, rcu): + return self._rcu_mode[rcu] + + def polarity(self, rcu): + """ + check polarity of signal + :param rcu: rcu number + :return: pol, 0=x, 1=y + """ + pol = None + if self._rcu_state[rcu]: + if self._rcu_mode[rcu] in (1, 2): + if rcu % 2 == 0: + pol = self.YPOL + else: + pol = self.XPOL + else: + if rcu % 2 == 0: + pol = self.XPOL + else: + pol = self.YPOL + return pol + + def rcus(self, band, polarity): + # logger.debug("band='%s' polarity='%s'" % (band, polarity)) + pol = None + if polarity in (0, 'X', 'x'): + pol = self.XPOL + if polarity in (1, 'Y', 'y'): + pol = self.YPOL + if polarity in (2, 'XY', 'xy'): + pol = self.XYPOL + if not pol: + return [] + # if not filled, fill it now + if len(self._rcus[band][pol]) == 0: + for rcu_nr, state in enumerate(self._rcu_state): + if state == 1: + if self._rcu_mode[rcu_nr] in self.bands[band]: + if pol is self.XYPOL: + self._rcus[band][pol].append(rcu_nr) + elif self.polarity(rcu_nr) == pol: + self._rcus[band][pol].append(rcu_nr) + #logger.debug("pol=%s selected rcus=%s" % (pol, ','.join([str(i) for i in sorted(self._rcus[band][pol])]))) + #logger.debug("pol=%s selected rcus=%s" % (pol, str(sorted(self._rcus[band][pol])).replace(' ','') )) + return sorted(self._rcus[band][pol]) + + def mask_rcu(self, rcus): + """ + mask rcu, this rcus will be ignored + :param rcus: list with rcus to mask + """ + if type(rcus) in (list, tuple): + for rcu in rcus: + if rcu not in self._rcu_mask: + self._rcu_mask.append(rcu) + else: + if rcus not in self._rcu_mask: + self._rcu_mask.append(rcus) + + def reset_masked_rcus(self): + self._rcu_mask = [] + + def reset_masked_sb(self, band): + self._sb_mask[band] = [] + + def set_passband(self, band, subbands): + """ + mask subbands, these subbands will be ignored + :param band: band to add + :param subbands: list with subbands to mask + """ + for sb in xrange(1,512,1): + if sb not in subbands: + if sb not in self._sb_mask: + self._sb_mask[band].append(sb) + + def mask_sb(self, band, subbands): + """ + mask subbands, these subbands will be ignored + :param band: band to add + :param subbands: list with subbands to mask + """ + for sb in subbands: + if sb not in self._sb_mask: + self._sb_mask[band].append(sb) + + def band_active(self, band): + """ + Checks if data available + :param band: band to check + :return: True if data available else False + """ + if self.rcus(band, 'xy'): + return True + return False + + def collect(self, n_seconds=2, slow=False): + """ + Collect new data + :param n_seconds: seconds to record + :param slow: get data i 2 steps + :return: None + """ + self._requested_seconds = n_seconds + self._reset() + self._get_rcu_info() + self._record_antenna_data(n_seconds, slow) + self._sbdata = self._read_files() + + def _get_rcu_info(self): + """ + get rcu information, state, mode and swapped + :return: + """ + n_rcus = int(self._args.get('n_rcus', 96)) + self._rcu_state = [0] * n_rcus # 0=Off, 1=On + self._rcu_mode = [0] * n_rcus # mode=0..7, 0=off + + # RCU[ 0].control=0x10337a9c => ON, mode:3, delay=28, att=06 + answer = rspctl("--rcu") + if answer.count('mode:') == n_rcus: + for line in answer.splitlines(): + if line.find('mode:') == -1: + continue + rcu = line[line.find('[') + 1: line.find(']')].strip() + state = line[line.find('=>') + 2: line.find(',')].strip() + mode = line[line.find('mode:') + 5] + if rcu.isdigit() and state in ("OFF", "ON") and mode.isdigit(): + if state == "OFF": + self._rcu_state[int(rcu)] = 0 + else: + self._rcu_state[int(rcu)] = 1 + self._rcu_mode[int(rcu)] = int(mode) + #logger.debug("rcu-info mode= %s" % str(self._rcu_mode)) + #logger.debug("rcu-info state= %s" % str(self._rcu_state)) + + def _record_antenna_data(self, n_seconds, slow): + """ + record antenna data using rspctl --statistics cmd, for all active rcus a file will be made + :param n_seconds: number of seconds to sample data + :param slow: slow down lcu disk usage + :return: + """ + self._remove_all_datafiles() # cleanup data directory + x_list = [] + y_list = [] + xy_list = [] + for rcu_nr, state in enumerate(self._rcu_state): + if state == 1: + xy_list.append(rcu_nr) + if rcu_nr % 2 == 0: + x_list.append(rcu_nr) + else: + y_list.append(rcu_nr) + + if slow is True: + rcus = select_str(x_list) + logger.debug("Wait %d seconds while recording X data" % n_seconds) + rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' % ( + n_seconds, self._data_dir, rcus), wait=0.0) + + rcus = select_str(y_list) + logger.debug("Wait %d seconds while recording Y data" % n_seconds) + rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' % ( + n_seconds, self._data_dir, rcus), wait=0.0) + else: + rcus = select_str(xy_list) + logger.debug("Wait %d seconds while recording XY data" % n_seconds) + rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' % ( + n_seconds, self._data_dir, rcus), wait=0.0) + + def _remove_all_datafiles(self): + """ + remove all *.dat files from data_dir + """ + logger.debug("testmode= %s" % str(is_test_mode_active())) + if not is_test_mode_active(): + if os.access(self._data_dir, os.F_OK): + files = os.listdir(self._data_dir) + # print files + for filename in files: + if filename[-3:] == 'dat' or filename[-3:] == 'nfo': + os.remove(os.path.join(self._data_dir, filename)) + + def _read_file(self, full_filename): + if not is_test_mode_active(): + sleep(0.02) + data = np.fromfile(full_filename, dtype=np.float64) + n_samples = len(data) + if (n_samples % 512) > 0: + logger.warning("data error: number of samples (%d) not multiple of 512 in '%f'" % ( + n_samples, full_filename)) + n_frames = n_samples / 512 + data = data.reshape(n_frames, 512) + #logger.info("recorded data shape %s" %(str(data.shape))) + return data[:self._requested_seconds,:] + + def _read_files(self): + files_in_dir = os.listdir(self._data_dir) + if len(files_in_dir) == 0: + logger.warning('No data recorded !!') + self._reset() + return + + data_shape = self._read_file(os.path.join(self._data_dir, files_in_dir[0])).shape + ssdata = np.zeros((self._n_rcus, data_shape[0], data_shape[1]), dtype=np.float64) + for file_name in sorted(files_in_dir): + # path, filename = os.split(file_name) + # filename format: 20160228_174114_sst_rcu000.dat + rcu = int(file_name.split('.')[0][-3:]) + ssdata[rcu, :, :] = self._read_file(os.path.join(self._data_dir, file_name)) + + # logger.debug("%s rcu=%d" %(file_name, rcu)) + + # mask zero values and convert to dBm + # logger.debug("rcu0=%s" % ssdata[:,0,301]) + ssdata_db = np.log10(np.ma.masked_less(ssdata, self._args.get('minvalue', 1.0))) * 10.0 + # do not use subband 0 + # logger.debug("rcu0=%s" % ssdata_db[:,0,301]) + ssdata_db[:, :, 0] = np.ma.masked + # logger.debug("rcu0=%s" % ssdata_db[:,0,301]) + logger.debug("recorded data shape %s" %(str(ssdata_db.shape))) + return ssdata_db + + # subbands is list to mask + def get_masked_data(self, band='', mask_subbands=True, mask_rcus=True): + data = self._sbdata.copy() + if mask_subbands: + data[:, :, self._sb_mask[band]] = np.ma.masked + if mask_rcus: + data[self._rcu_mask, :, :] = np.ma.masked + return data + + # spectra(s) for one rcu + def rcu_median_spectra(self, rcu, masked): + spec = self.rcu_spectras(rcu, masked) + if spec.shape[0] > 1: + return np.median(spec, axis=0) + return spec[0,:] + + + def rcu_mean_spectra(self, rcu, masked): + spec = self.rcu_spectras(rcu, masked) + if spec.shape[0] > 1: + return np.mean(spec, axis=0) + return spec[0,:] + + + def rcu_spectras(self, rcu, masked): + if rcu in range(self._n_rcus): + if masked: + return self.get_masked_data(band=mode_to_band(self.mode(rcu)), mask_rcus=False)[rcu, :, :] + else: + return self._sbdata[rcu, :, :] + + logger.error("Not valid arguments %s" % ( + str(rcu))) + return None + + # spectras for one band and polarity + def mean_spectras(self, freq_band, polarity, masked): + spec = self.spectras(freq_band, polarity, masked) + if spec.shape[1] > 1: + return np.mean(spec, axis=1) + return spec[:,0,:] + + def mean_all_spectras(self, freq_band, polarity, masked): + spec = self.mean_spectras(freq_band, polarity, masked) + if spec.shape[0] > 1: + return np.mean(spec, axis=0) + return spec[0,:] + + def median_spectras(self, freq_band, polarity, masked): + spec = self.spectras(freq_band, polarity, masked) + if spec.shape[1] > 1: + return np.median(spec, axis=1) + return spec[:,0,:] + + def median_all_spectras(self, freq_band, polarity, masked): + spec = self.median_spectras(freq_band, polarity, masked) + if spec.shape[0] > 1: + return np.median(spec, axis=0) + return spec[0,:] + + def spectras(self, freq_band, polarity, masked): + return self.subbands(freq_band, polarity, range(512), masked) + + def subbands(self, freq_band, polarity, sb_set, masked): + sb_range = range(512) + pol = None + band = None + if polarity in (0, 'X', 'x'): + pol = self.XPOL + if polarity in (1, 'Y', 'y'): + pol = self.YPOL + if polarity in (2, 'XY', 'xy'): + pol = self.XYPOL + if freq_band in self.bands.keys(): + band = freq_band + + if isinstance(sb_set, int): + if sb_set in sb_range: + sb = sb_set + else: + sb = None + else: + sb = list(sb_set) + for i in sb: + if i not in sb_range: + sb = None + + if pol and band and sb: + rcu_list = self.rcus(band, pol) + #logger.debug("rcu-list=%s" % str(rcu_list)) + # logger.debug("sb-list=%s" % str(sb)) + if masked: + masked_data = self.get_masked_data(band=band) + rcu_data = masked_data[rcu_list, :, :] + sb_data = rcu_data[:, :, sb] + # logger.debug("subbands():: sb_data.shape=%s" % str(sb_data.shape)) + # logger.debug("subbands():: sb_data= %s" % str(sb_data)) + return sb_data + else: + rcu_data = self._sbdata[rcu_list, :, :] + sb_data = rcu_data[:, :, sb] + # logger.debug("subbands():: sb_data.shape=%s" % str(sb_data.shape)) + # logger.debug("subbands():: sb_data= %s" % str(sb_data)) + return sb_data + + logger.error("Not valid arguments %s, %s, %s" % (str(band), + str(polarity), + str(sb_set))) + return None diff --git a/LCU/checkhardware/checkhardware_lib/db.py b/LCU/checkhardware/checkhardware_lib/db.py new file mode 100644 index 0000000000000000000000000000000000000000..0fbf81bcdf39b698dd17332f7447cf9988a6a568 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/db.py @@ -0,0 +1,531 @@ +#!/usr/bin/python + +from copy import deepcopy +from general import * +from lofar import * +import time +import logging +import string + +db_version = '0415' + +logger = logging.getLogger('main.db') +logger.debug("starting db logger") + + +class DB: + def __init__(self, StID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT): + self.StID = StID + self.nr_rsp = nRSP + self.nr_spu = nRSP / 4 + self.nr_rcu = nRSP * 8 + self.nr_lbl = nLBL + self.nr_lbh = nLBH + self.nr_hba = nHBA + self.hba_split = HBA_SPLIT + self.nr_tbb = nTBB + + self.script_versions = '' + + self.board_errors = list() + self.rcumode = -1 + self.tests_done = list() + self.check_start_time = 0 + self.check_stop_time = 0 + self.rsp_driver_down = False + self.tbb_driver_down = False + + self.station_error = 0 + + self.test_end_time = -1 + self.rcus_changes = False + + self.spu = list() + for i in range(self.nr_spu): + self.spu.append(self.SPU(i)) + + self.rsp = list() + for i in range(nRSP): + self.rsp.append(self.RSP(i)) + + self.tbb = list() + for i in range(nTBB): + self.tbb.append(self.TBB(i)) + + self.rcu_state = list() + for i in range(self.nr_rcu): + self.rcu_state.append(0) + + self.lbl = deepcopy(self.LBA(label='LBL', nr_antennas=nLBL, nr_offset=48)) + self.lbh = deepcopy(self.LBA(label='LBH', nr_antennas=nLBH, nr_offset=0)) + self.hba = deepcopy(self.HBA(nr_tiles=nHBA, split=self.hba_split)) + + def set_test_end_time(self, end_time): + if end_time > time.time(): + self.test_end_time = end_time + else: + logger.warning("end time in past") + return + + # returns True if before end time + def check_end_time(self, duration=0.0): + if self.test_end_time == -1: + return True + if (time.time() + duration) < self.test_end_time: + return True + else: + return False + + # add only ones + def add_test_done(self, name): + if name not in self.tests_done: + self.tests_done.append(name) + + # check if already done + def is_test_done(self, name): + if name in self.tests_done: + return False + return True + + # test + def test(self): + for _spu in self.spu: + ok = _spu.test() + if not ok: + self.station_error = 1 + + for _rsp in self.rsp: + ok = _rsp.test() + if not ok: + self.station_error = 1 + + for _tbb in self.tbb: + ok = _tbb.test() + if not ok: + self.station_error = 1 + + # test rcu's first + for _rcu in range(self.nr_rcu): + error_count = 0 + + ant_nr = _rcu / 2 + pol_nr = _rcu % 2 # 0=X, 1=Y + + if pol_nr == 0: + if self.nr_lbl > 0 and ant_nr < self.nr_lbl and self.lbl.ant[ant_nr].x.error: + error_count += 1 + if ant_nr < self.nr_lbh and self.lbh.ant[ant_nr].x.error: + error_count += 1 + if ant_nr < self.nr_hba and self.hba.tile[ant_nr].x.rcu_error: + error_count += 1 + else: + if self.nr_lbl > 0 and ant_nr < self.nr_lbl and self.lbl.ant[ant_nr].y.error: + error_count += 1 + if ant_nr < self.nr_lbh and self.lbh.ant[ant_nr].y.error: + error_count += 1 + if ant_nr < self.nr_hba and self.hba.tile[ant_nr].y.rcu_error: + error_count += 1 + + if error_count >= 2: + self.rcu_state[_rcu] = 1 + + self.station_error = max(self.station_error, self.lbl.test(), self.lbh.test(), self.hba.test()) + + return self.station_error + + + # ======================================================================================================================= + # database from here + class SPU: + def __init__(self, nr): + self.nr = nr + self.rcu_5_0_volt = 0.0 + self.lba_8_0_volt = 0.0 + self.hba_48_volt = 0.0 + self.spu_3_3V = 0.0 + self.rcu_ok = 1 + self.lba_ok = 1 + self.hba_ok = 1 + self.spu_ok = 1 + self.voltage_ok = 1 + self.temp = 0.0 + self.temp_ok = 1 + + def test(self): + self.voltage_ok = 0 + if self.rcu_ok and self.lba_ok and self.hba_ok and self.spu_ok: + self.voltage_ok = 1 + return self.voltage_ok + + class RSP: + def __init__(self, nr): + self.nr = nr + + self.test_done = 0 + self.board_ok = 1 + self.ap_version = 'ok' + self.bp_version = 'ok' + self.version_ok = 1 + self.voltage1_2 = 0.0 + self.voltage2_5 = 0.0 + self.voltage3_3 = 0.0 + self.voltage_ok = 1 + self.pcb_temp = 0.0 + self.bp_temp = 0.0 + self.ap0_temp = 0.0 + self.ap1_temp = 0.0 + self.ap2_temp = 0.0 + self.ap3_temp = 0.0 + self.temp_ok = 1 + + def test(self): + if self.ap_version != 'ok' or self.bp_version != 'ok': + self.version_ok = 0 + return self.version_ok and self.voltage_ok and self.temp_ok + + # used by LBA and HBA antenna class + class Polarity: + def __init__(self, rcu=None): + self.rcu = rcu + self.rcu_off = 0 # 0 = RCU on, 1 = RCU off + self.rcu_error = 0 + + # status variables 0|1 + self.error = 0 # + self.too_low = 0 # + self.too_high = 0 # + self.low_noise = 0 # + self.high_noise = 0 # + self.jitter = 0 # + self.osc = 0 # + self.no_signal = 0 # signal below 2dB + self.summator_noise = 0 # + self.spurious = 0 # + self.flat = 0 # + self.short = 0 # + + # test result of signal test, + # only for HBA element test, first value ctrl=129 second value ctrl=253 + self.rf_test_subband = [0, 0] + self.rf_ref_signal = [-1, -1] + self.test_signal = [0.0, 0.0] + + # for down test + self.down_pwr = 0.0 + self.down_offset = 0 + + # measured values filled on error + # proc : bad time in meausured time 0..100% + # val : max or min meausured value + self.low_seconds = 0 + self.low_bad_seconds = 0 + self.low_val = 100.0 # + self.low_diff = 0.0 + self.low_ref = 0.0 # + + self.high_seconds = 0 + self.high_bad_seconds = 0 + self.high_val = 0.0 # + self.high_diff = 0.0 + self.high_ref = 0.0 # + + self.jitter_seconds = 0 + self.jitter_bad_seconds = 0 + self.jitter_val = 0.0 + self.jitter_ref = 0.0 + + self.flat_val = 0.0 + self.short_val = 0.0 + + class LBA: + def __init__(self, label, nr_antennas, nr_offset=0): + self.rsp_driver_down = False + self.noise_check_done = 0 + self.signal_check_done = 0 + self.short_check_done = 0 + self.flat_check_done = 0 + self.down_check_done = 0 + self.spurious_check_done = 0 + self.oscillation_check_done = 0 + + self.noise_low_deviation = 0.0 + self.noise_high_deviation = 0.0 + self.noise_max_fluctuation = 0.0 + + self.rf_low_deviation = 0.0 + self.rf_high_deviation = 0.0 + self.rf_subband = 0 + + self.check_time_noise = 0 + self.nr_antennas = nr_antennas + self.nr_offset = nr_offset + self.label = label + self.error = 0 + self.rf_signal_to_low = 0 + self.avg_x = 0 + self.avg_y = 0 + self.rf_test_subband_x = 0 + self.rf_test_subband_y = 0 + self.rf_ref_signal_x = 0 + self.rf_ref_signal_y = 0 + self.nr_bad_antennas = -1 + self.ant = list() + for i in range(self.nr_antennas): + self.ant.append(self.Antenna(i, self.nr_offset)) + return + + def test(self): + if self.rsp_driver_down: + return self.error + if self.noise_check_done or self.signal_check_done or self.short_check_done or \ + self.flat_check_done or self.down_check_done or self.signal_check_done or \ + self.spurious_check_done or self.oscillation_check_done: + self.nr_bad_antennas = 0 + + for ant in self.ant: + ant.test() + ant_error = max(ant.x.error, ant.y.error) + self.error = max(self.error, ant_error) + if ant_error: + self.nr_bad_antennas += 1 + return self.error + + # return select string for rspctl command + def select_list(self): + select = list() + for ant in self.ant: + if ant.on_bad_list == 0: + select.append(ant.x.rcu) + select.append(ant.y.rcu) + return select + + def reset_rcu_state(self): + for ant in self.ant: + ant.x.rcu_off = 0 + ant.y.rcu_off = 0 + + class Antenna: + def __init__(self, nr, nr_offset): + self.nr = nr + self.nr_pvss = self.nr + nr_offset + self.on_bad_list = 0 + if nr_offset == 0: + self.x = DB.Polarity(rcu=(self.nr * 2)) + self.y = DB.Polarity(rcu=((self.nr * 2) + 1)) + else: + self.x = DB.Polarity(rcu=(self.nr * 2) + 1) + self.y = DB.Polarity(rcu=((self.nr * 2))) + self.down = 0 + return + + def test(self): + self.x.error = max(self.x.too_low, self.x.too_high, self.x.osc, self.x.high_noise, self.x.low_noise, + self.x.jitter, self.x.spurious, self.down, self.x.flat, self.x.short) + self.y.error = max(self.y.too_low, self.y.too_high, self.y.osc, self.y.high_noise, self.y.low_noise, + self.y.jitter, self.y.spurious, self.down, self.y.flat, self.y.short) + return + + class HBA: + def __init__(self, nr_tiles, split): + self.rsp_driver_down = False + self.modem_check_done = 0 + self.noise_check_done = 0 + self.signal_check_done = 0 + self.spurious_check_done = 0 + self.oscillation_check_done = 0 + self.summatornoise_check_done = 0 + self.element_check_done = 0 + + self.hba_split = split + self.check_time_noise = 0 + self.check_time_noise_elements = 0 + self.nr_tiles = nr_tiles + self.error = 0 + self.rf_signal_to_low = 0 + # only used for tile RF test + # first value ctrl=129 second value ctrl=253 + self.rf_test_subband_x = [0, 0] + self.rf_test_subband_y = [0, 0] + self.rf_ref_signal_x = [0.0, 0.0] + self.rf_ref_signal_y = [0.0, 0.0] + self.tile = list() + self.nr_bad_tiles = -1 + self.nr_bad_tiles_0 = -1 + self.nr_bad_tiles_1 = -1 + for i in range(self.nr_tiles): + self.tile.append(self.Tile(i)) + return + + def test(self): + if self.rsp_driver_down: + return self.error + if self.modem_check_done or self.noise_check_done or self.signal_check_done or self.spurious_check_done or\ + self.oscillation_check_done or self.summatornoise_check_done or self.element_check_done: + if self.hba_split == 1: + self.nr_bad_tiles_0 = 0 + self.nr_bad_tiles_1 = 0 + else: + self.nr_bad_tiles = 0 + + for tile in self.tile: + tile.test(self.element_check_done or self.modem_check_done) + tile_error = max(tile.x.error, tile.y.error) + self.error = max(self.error, tile_error) + + if tile_error: + if self.hba_split == 1: + if tile.nr < 24: + self.nr_bad_tiles_0 += 1 + else: + self.nr_bad_tiles_1 += 1 + else: + self.nr_bad_tiles += 1 + return self.error + + # return select string for rspctl command + def select_list(self): + select = list() + for tile in self.tile: + if tile.on_bad_list == 0: + select.append(tile.x.rcu) + select.append(tile.y.rcu) + return select + + def reset_rcu_state(self): + for tile in self.tile: + tile.x.rcu_off = 0 + tile.y.rcu_off = 0 + + class Tile: + def __init__(self, nr): + self.nr = nr + self.on_bad_list = 0 + self.x = DB.Polarity(rcu=(nr * 2)) + self.y = DB.Polarity(rcu=(nr * 2 + 1)) + + self.noise_low_deviation = 0.0 + self.noise_high_deviation = 0.0 + self.noise_max_fluctuation = 0.0 + + self.rf_low_deviation = 0.0 + self.rf_high_deviation = 0.0 + self.rf_subband = 0 + + self.no_power = 0 # signal around 60dB + self.p_summator_error = 0 + self.c_summator_error = 0 + self.nr_elements = 16 + self.element = list() + for i in range(1, self.nr_elements + 1, 1): + self.element.append(self.Element(i)) + return + + def test(self, check_done): + no_modem_cnt = 0 + modem_err_cnt = 0 + no_power_cnt = 0 + x_no_signal_cnt = 0 + y_no_signal_cnt = 0 + if check_done: + for elem in self.element: + elem.test() + if elem.x.no_signal: + x_no_signal_cnt += 1 + if elem.y.no_signal: + y_no_signal_cnt += 1 + if elem.no_power: + no_power_cnt += 1 + if elem.no_modem: + no_modem_cnt += 1 + if elem.modem_error: + modem_err_cnt += 1 + + self.x.error = max(self.x.error, elem.x.error) + self.y.error = max(self.y.error, elem.y.error) + + if (no_modem_cnt >= 8) or (modem_err_cnt >= 8): + self.c_summator_error = 1 + if no_power_cnt >= 15: + self.p_summator_error = 1 + if x_no_signal_cnt == 16: + self.x.rcu_error = 1 + if y_no_signal_cnt == 16: + self.y.rcu_error = 1 + + self.x.error = max(self.x.error, self.x.too_low, self.x.too_high, self.x.low_noise, self.x.no_signal, + self.x.high_noise, self.x.jitter, self.x.osc, + self.x.summator_noise, self.x.spurious, self.p_summator_error, self.c_summator_error) + + self.y.error = max(self.y.error, self.y.too_low, self.y.too_high, self.y.low_noise, self.y.no_signal, + self.y.high_noise, self.y.jitter, self.y.osc, + self.y.summator_noise, self.y.spurious, self.p_summator_error, self.c_summator_error) + return + + class Element: + def __init__(self, nr): + self.nr = nr + self.x = DB.Polarity() + self.y = DB.Polarity() + + self.noise_low_deviation = 0.0 + self.noise_high_deviation = 0.0 + self.noise_max_fluctuation = 0.0 + + self.rf_low_deviation = 0.0 + self.rf_high_deviation = 0.0 + self.rf_subband = 0 + + self.no_power = 0 # signal around 60dB + self.no_modem = 0 # modem reponse = ?? + self.modem_error = 0 # wrong response from modem + + return + + def test(self): + modem_err = 0 + if self.no_modem or self.modem_error: + modem_err = 1 + + self.x.error = max(self.x.too_low, self.x.too_high, self.x.low_noise, self.x.high_noise, + self.x.no_signal, + self.x.jitter, self.no_power, self.x.spurious, self.x.osc, modem_err) + + self.y.error = max(self.y.too_low, self.y.too_high, self.y.low_noise, self.y.high_noise, + self.y.no_signal, + self.y.jitter, self.no_power, self.y.spurious, self.y.osc, modem_err) + return + + class TDS: + def __init__(self): + self.test_done = 0 + self.ok = 1 + + class TBB: + def __init__(self, nr): + self.nr = nr + self.board_active = 1 + self.board_ok = 1 + self.test_done = 0 + self.tp_version = 'ok' + self.mp_version = 'ok' + self.memory_size = 0 + self.version_ok = 1 + self.memory_ok = 1 + self.voltage1_2 = 0.0 + self.voltage2_5 = 0.0 + self.voltage3_3 = 0.0 + self.voltage_ok = 1 + self.pcb_temp = 0.0 + self.tp_temp = 0.0 + self.mp0_temp = 0.0 + self.mp1_temp = 0.0 + self.mp2_temp = 0.0 + self.mp3_temp = 0.0 + self.temp_ok = 1 + + def test(self): + if self.tp_version != 'ok' or self.mp_version != 'ok': + self.version_ok = 0 + if self.memory_size != 0: + self.memory_ok = 0 + return self.version_ok and self.voltage_ok and self.temp_ok diff --git a/LCU/checkhardware/checkhardware_lib/general.py b/LCU/checkhardware/checkhardware_lib/general.py new file mode 100644 index 0000000000000000000000000000000000000000..b018fd01db2f6a8dcff695180cba02bf09332dcb --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/general.py @@ -0,0 +1,146 @@ +r""" +general script +""" + +from subprocess import (Popen, PIPE) +import traceback +import time +import os +import sys +import logging + +general_version = '0913' +logger = logging.getLogger('main.general') +logger.debug("starting general logger") + + +def write_message(msg): + run_cmd('wall %s' % str(msg)) + return + + +# Return date string in the following format YYYYMMDD +def get_short_date_str(tm=time.gmtime()): + return time.strftime("%Y%m%d", tm) + + +# Return time string in the following format HH:MM:SS +def get_date_str(tm=time.gmtime()): + return time.strftime("%d-%m-%Y", tm) + + +# Return time string in the following format HH:MM:SS +def get_time_str(tm=time.gmtime()): + return time.strftime("%H:%M:%S", tm) + + +# Return time string in the following format HH:MM:SS +def get_date_time_str(tm=time.gmtime()): + return time.strftime("%d-%m-%YT%H:%M:%S", tm) + + +# Run cmd with args and return response +def run_cmd(cmd=''): + if cmd != '': + try: + _cmd = cmd.replace(' =', '=').replace('= ', '=') + cmd_list = _cmd.split() + #print cmd_list + cmdline = Popen(cmd_list, stdout=PIPE, stderr=PIPE) + (so, se) = cmdline.communicate() + if len(so) != 0: + return so + else: + return 'Error, %s' % se + except: + logger.error('Caught %s', str(sys.exc_info()[0])) + logger.error(str(sys.exc_info()[1])) + logger.error('TRACEBACK:\n%s', traceback.format_exc()) + return 'Exception Error' + + return '' + + +# Get Host name +def get_hostname(): + retries = 0 + while retries < 3: + try: + host = run_cmd('hostname -s') + if host == 'Exception Error': + host = 'Unknown' + retries += 1 + if host != 'Unknown': + break + except: + host = 'Unknown' + retries += 1 + + return host.strip() + + +# file logger +class MyLogger: + def __init__(self, logdir, filename, screen_prefix=''): + self.fullFilename = os.path.join(logdir, filename) + self.logfile = open(self.fullFilename, 'w') + self.prefix = screen_prefix + self.start_time = time.time() + + def __del__(self): + self.logfile.close() + + def get_full_filename(self): + return self.fullFilename + + def reset_start_time(self, screen=False): + self.start_time = time.time() + self.info("Start time %s" % (time.strftime("%H:%M:%S", time.gmtime(self.start_time))), screen=screen) + + def print_busy_time(self, screen=False): + self.info("Time from start %s" % (time.strftime("%H:%M:%S", (time.gmtime(time.time() - self.start_time)))), + screen=screen) + + def print_time_now(self, screen=False): + self.info("Time %s" % (time.strftime("%H:%M:%S", time.gmtime(time.time()))), screen=screen) + + def info(self, msg, no_end=False, screen=False): + if len(msg) != 0: + if screen: + print self.prefix + ' ' + msg + if not no_end: + msg += '\n' + self.logfile.write(msg) + self.logfile.flush() + + +class MyTestLogger(MyLogger): + def __init__(self, logdir, hostname): + filename = '%s_station_test.csv' % hostname.upper() + MyLogger.__init__(self, logdir, filename) + + def add_line(self, info): + MyLogger.info(self, info) + + +class MyStationLogger(MyLogger): + def __init__(self, logdir, hostname, filetime=time.gmtime()): + filename = "stationtest_%s.log" % hostname + MyLogger.__init__(self, logdir, filename) + MyLogger.info(self, "StID >: %s" % hostname) + MyLogger.info(self, "Lgfl >: %s" % (os.path.join(logdir, filename))) + testdate = time.strftime("%a, %d %b %Y %H:%M:%S", filetime) + MyLogger.info(self, "Time >: %s" % testdate) + + def add_line(self, info): + MyLogger.info(self, info) + + +class MyPVSSLogger(MyLogger): + def __init__(self, logdir, hostname): + filename = '%s_station_test_pvss.log' % hostname + MyLogger.__init__(self, logdir, filename) + # cLogger.info(self, "# PVSS input file") + + def add_line(self, info): + MyLogger.info(self, info) diff --git a/LCU/checkhardware/checkhardware_lib/hardware_tests.py b/LCU/checkhardware/checkhardware_lib/hardware_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..6d58fa4b31e3276957165fd33dc788a2fd35ac54 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/hardware_tests.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +# test lib + +from checkhardware_lib.spectrum_checks import * +from data import * +from lofar import * + +test_version = '0815' + +logger = None + + +def init_test_lib(): + global logger + logger = logging.getLogger() + logger.debug("init logger test_lib") + + +# HBASubband = dict( DE601C=155, DE602C=155, DE603C=284, DE604C=474, DE605C=479, FR606C=155, SE607C=287, UK608C=155 ) +# DefaultLBASubband = 301 +# DefaultHBASubband = 155 + + + + + + + + + + + diff --git a/LCU/checkhardware/checkhardware_lib/hba.py b/LCU/checkhardware/checkhardware_lib/hba.py new file mode 100644 index 0000000000000000000000000000000000000000..60cb30ef4488d5f98f3549fee382d318ba4d1234 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/hba.py @@ -0,0 +1,829 @@ +import logging +from data import AntennaData +from spectrum_checks import * +from lofar import * + +logger = logging.getLogger('main.hba') +logger.debug("starting hba logger") + +# class for testing HBA antennas +class HBA(object): + def __init__(self, db, hba): + self.db = db + self.hba = hba + self.antenna_data = AntennaData({'n_rcus': hba.nr_tiles * 2, 'data-dir': data_dir()}) + self.rcumode = 0 + + def reset(self): + self.db.rcus_changed = False + + def turn_on_tiles(self): + pass + + def turn_off_tile(self, tile_nr): + tile = self.hba.tile[tile_nr] + tile.x.rcu_off = 1 + tile.y.rcu_off = 1 + logger.info("turned off tile %d RCU(%d,%d)" % (tile.nr, tile.x.rcu, tile.y.rcu)) + rspctl("--rcumode=0 --select=%d,%d" % (tile.x.rcu, tile.y.rcu), wait=2.0) + self.db.rcus_changed = True + return + + def turn_off_bad_tiles(self): + for tile in self.hba.tile: + if tile.x.rcu_off and tile.y.rcu_off: + continue + no_modem = 0 + modem_error = 0 + for elem in tile.element: + if elem.no_modem: + no_modem += 1 + if elem.modem_error: + modem_error += 1 + if tile.x.osc or tile.y.osc or (no_modem >= 8) or (modem_error >= 8): + self.turn_off_tile(tile.nr) + return + + def set_mode(self, mode): + if self.db.rcumode != mode: + self.db.rcumode = mode + turn_off_rcus() + turn_on_rcus(mode=mode, rcus=self.hba.select_list()) + self.hba.reset_rcu_state() + + def record_data(self, rec_time, new_data=False): + if new_data or self.db.rcus_changed or self.antenna_data.seconds() < rec_time: + logger.debug('record info changed') + self.db.rcus_changed = False + self.antenna_data.collect(n_seconds=rec_time) + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + self.antenna_data.mask_rcu([tile.x.rcu, tile.y.rcu]) + + + def check_modem(self, mode): + # setup internal test db + n_elements = 16 + n_tests = 7 + modem_tst = list() + for tile_nr in range(self.db.nr_hba): + tile = list() + for elem_nr in range(n_elements): + test = list() + for tst_nr in range(n_tests): + test.append([0, 0]) + tile.append(test) + modem_tst.append(tile) + # done + + logger.info("=== Start HBA modem test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=50.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + time.sleep(4.0) + ctrlstr = list() + ctrlstr.append(('129,' * 16)[:-1]) # 0ns + ctrlstr.append(('133,' * 16)[:-1]) # 0.5ns + ctrlstr.append(('137,' * 16)[:-1]) # 1ns + ctrlstr.append(('145,' * 16)[:-1]) # 2ns + ctrlstr.append(('161,' * 16)[:-1]) # 4ns + ctrlstr.append(('193,' * 16)[:-1]) # 8ns + ctrlstr.append(('253,' * 16)[:-1]) # 15.5ns + # rsp_hba_delay(delay=ctrlstr[6], rcus=self.hba.selectList(), discharge=False) + tst_nr = 0 + for ctrl in ctrlstr: + + rsp_hba_delay(delay=ctrl, rcus=self.hba.select_list(), discharge=False) + #data = rspctl('--realdelays', wait=1.0).splitlines() + data = rspctl('--realdelays', wait=0.0).splitlines() + + ctrllist = ctrl.split(',') + for line in data: + if line[:3] == 'HBA': + rcu = int(line[line.find('[') + 1:line.find(']')]) + hba_nr = rcu / 2 + if hba_nr >= self.hba.nr_tiles: + continue + if self.hba.tile[hba_nr].on_bad_list: + continue + realctrllist = line[line.find('=') + 1:].strip().split() + for elem in self.hba.tile[hba_nr].element: + if ctrllist[elem.nr - 1] != realctrllist[elem.nr - 1]: + logger.info("Modemtest Tile=%d RCU=%d Element=%d ctrlword=%s response=%s" % ( + hba_nr, rcu, elem.nr, ctrllist[elem.nr - 1], realctrllist[elem.nr - 1])) + + if realctrllist[elem.nr - 1].count('?') == 3: + # elem.no_modem += 1 + modem_tst[hba_nr][elem.nr - 1][tst_nr][0] = 1 + else: + # elem.modem_error += 1 + modem_tst[hba_nr][elem.nr - 1][tst_nr][1] = 1 + tst_nr += 1 + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + # analyse test results and add to DB + no_modem = dict() + modem_error = dict() + for tile_nr in range(self.db.nr_hba): + n_no_modem = dict() + n_modem_error = dict() + for elem_nr in range(n_elements): + n_no_modem[elem_nr] = 0 + n_modem_error[elem_nr] = 0 + for tst_nr in range(n_tests): + if modem_tst[tile_nr][elem_nr][tst_nr][0]: + n_no_modem[elem_nr] += 1 + if modem_tst[tile_nr][elem_nr][tst_nr][1]: + n_modem_error[elem_nr] += 1 + no_modem[tile_nr] = n_no_modem + modem_error[tile_nr] = n_modem_error + + n_tile_err = 0 + for tile in no_modem: + n_elem_err = 0 + for elem in no_modem[tile]: + if no_modem[tile][elem] == n_tests: + n_elem_err += 1 + if n_elem_err == n_elements: + n_tile_err += 1 + + if n_tile_err < (self.db.nr_hba / 2): + for tile_nr in range(self.db.nr_hba): + for elem_nr in range(n_elements): + #if no_modem[tile_nr][elem_nr] >= 2: # 2 or more ctrl values went wrong + if no_modem[tile_nr][elem_nr]: # 1 or more ctrl values went wrong + self.db.hba.tile[tile_nr].element[elem_nr].no_modem = 1 + + n_tile_err = 0 + for tile in modem_error: + n_elem_err = 0 + for elem in modem_error[tile]: + if modem_error[tile][elem] == n_tests: + n_elem_err += 1 + if n_elem_err == n_elements: + n_tile_err += 1 + + if n_tile_err < (self.db.nr_hba / 2): + for tile_nr in range(self.db.nr_hba): + for elem_nr in range(n_elements): + #if no_modem[tile_nr][elem_nr] >= 2: # 2 or more ctrl values went wrong + if no_modem[tile_nr][elem_nr]: # 1 or more ctrl values went wrong + self.db.hba.tile[tile_nr].element[elem_nr].modem_error = 1 + + self.hba.modem_check_done = 1 + self.db.add_test_done('M%d' % mode) + logger.info("=== Done HBA modem test ===") + # self.db.rcumode = 0 + return + + # check for summator noise and turn off RCU + def check_summator_noise(self, mode, parset): + logger.info("=== Start HBA tile based summator-noise test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=25.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + delay_str = ('253,' * 16)[:-1] + rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + self.record_data(rec_time=12) + + for pol_nr, pol in enumerate(('X', 'Y')): + sum_noise = check_for_summator_noise(data=self.antenna_data, band=mode_to_band(mode), + pol=pol, parset=parset) + for n in sum_noise: + rcu, cnt, n_peaks = n + tile = rcu / 2 + logger.info("RCU %d Tile %d Summator-Noise cnt=%3.1f peaks=%3.1f" % ( + rcu, tile, cnt, n_peaks)) + if pol == 'X': + self.hba.tile[tile].x.summator_noise = 1 + else: + self.hba.tile[tile].y.summator_noise = 1 + self.turn_off_tile(tile) + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.summatornoise_check_done = 1 + self.db.add_test_done('SN%d' % mode) + logger.info("=== Done HBA tile based summator-noise test ===") + return + + # check for oscillating tiles and turn off RCU + # stop one RCU each run + def check_oscillation(self, mode, parset): + logger.info("=== Start HBA tile based oscillation test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=35.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + delay_str = ('253,' * 16)[:-1] + get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + clean = False + while not clean: + if not self.db.check_end_time(duration=25.0): + logger.warning("check stopped, end time reached") + return + + clean = True + + self.record_data(rec_time=12, new_data=get_new_data) + + for pol_nr, pol in enumerate(('X', 'Y')): + # result is a sorted list on maxvalue + result = check_for_oscillation(data=self.antenna_data, band=mode_to_band(mode), pol=pol, parset=parset) + max_sum = n_peaks = 0 + if len(result) > 1: + if len(result) == 2: + rcu, max_sum, n_peaks, rcu_low = result[1] + else: + ref_low = result[0][3] + max_low_tile = (-1, -1) + max_sum_tile = (-1, -1) + for i in result[1:]: + rcu, max_sum, n_peaks, tile_low = i + # rcu = (tile * 2) + pol_nr + if max_sum > max_sum_tile[0]: + max_sum_tile = (max_sum, rcu) + if (tile_low - ref_low) > max_low_tile[0]: + max_low_tile = (tile_low, rcu) + + rcu_low, rcu = max_low_tile + + clean = False + get_new_data = True + tile = rcu / 2 + # tile_polarity = rcu % 2 + # rcu = (tile * 2) + pol_nr + logger.info("RCU %d Tile %d Oscillation sum=%3.1f peaks=%d low=%3.1f" % ( + rcu, tile, max_sum, n_peaks, rcu_low)) + self.turn_off_tile(tile) + if pol_nr == 0: + self.hba.tile[tile].x.osc = 1 + else: + self.hba.tile[tile].y.osc = 1 + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.oscillation_check_done = 1 + self.db.add_test_done('O%d' % mode) + logger.info("=== Done HBA tile based oscillation test ===") + return + + def check_noise(self, mode, record_time, parset): + logger.info("=== Start HBA tile based noise test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=(record_time + 60.0)): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + logger.info("skip low-noise test for tile %d, RCUs turned off" % tile.nr) + + delay_str = ('253,' * 16)[:-1] + get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + self.record_data(rec_time=record_time, new_data=get_new_data) + + for pol_nr, pol in enumerate(('X', 'Y')): + # result is a sorted list on maxvalue + low_noise, high_noise, jitter = check_for_noise(data=self.antenna_data, band=mode_to_band(mode), pol=pol, + parset=parset) + + for n in low_noise: + rcu, val, bad_secs, ref, diff = n + tile = rcu / 2 + if self.hba.tile[tile].x.rcu_off or self.hba.tile[tile].y.rcu_off: + continue + logger.info("RCU %d Tile %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" % ( + rcu, tile, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + if pol == 'X': + tile_polarity = self.hba.tile[tile].x + else: + tile_polarity = self.hba.tile[tile].y + + tile_polarity.low_seconds += self.antenna_data.seconds() + tile_polarity.low_bad_seconds += bad_secs + if val < tile_polarity.low_val: + tile_polarity.low_noise = 1 + tile_polarity.low_val = val + tile_polarity.low_ref = ref + tile_polarity.low_diff = diff + + for n in high_noise: + rcu, val, bad_secs, ref, diff = n + tile = rcu / 2 + logger.info("RCU %d Tile %d High-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.1f" % ( + rcu, tile, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + if pol == 'X': + tile_polarity = self.hba.tile[tile].x + else: + tile_polarity = self.hba.tile[tile].y + + tile_polarity.high_seconds += self.antenna_data.seconds() + tile_polarity.high_bad_seconds += bad_secs + if val > tile_polarity.high_val: + tile_polarity.high_noise = 1 + tile_polarity.high_val = val + tile_polarity.high_ref = ref + tile_polarity.high_diff = diff + + for n in jitter: + rcu, val, ref, bad_secs = n + tile = rcu / 2 + logger.info("RCU %d Tile %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" % (rcu, tile, val, ref)) + + if pol == 'X': + tile_polarity = self.hba.tile[tile].x + else: + tile_polarity = self.hba.tile[tile].y + + tile_polarity.jitter_seconds += self.antenna_data.seconds() + tile_polarity.jitter_bad_seconds += bad_secs + if val > tile_polarity.jitter_val: + tile_polarity.jitter = 1 + tile_polarity.jitter_val = val + tile_polarity.jitter_ref = ref + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.noise_check_done = 1 + self.db.add_test_done('NS%d=%d' % (mode, record_time)) + logger.info("=== Done HBA tile based noise test ===") + return + + def check_spurious(self, mode, parset): + logger.info("=== Start HBA tile based spurious test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=12.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + delay_str = ('253,' * 16)[:-1] + get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + self.record_data(rec_time=12, new_data=get_new_data) + + for pol_nr, pol in enumerate(('X', 'Y')): + # result is a sorted list on maxvalue + result = check_for_spurious(data=self.antenna_data, band=mode_to_band(mode), pol=pol, parset=parset) + for rcu in result: + tile = rcu / 2 + logger.info("RCU %d Tile %d pol %c Spurious" % (rcu, tile, pol)) + if pol == 'X': + self.hba.tile[tile].x.spurious = 1 + else: + self.hba.tile[tile].y.spurious = 1 + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.spurious_check_done = 1 + self.db.add_test_done('SP%d' % mode) + logger.info("=== Done HBA spurious test ===") + return + + def check_rf_power(self, mode, parset): + logger.info("=== Start HBA tile based RF test ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=37.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + # check twice + # 2 ... check if all elements are turned off, normal value between 60.0 and 62.0 + # 128 ... + # 253 ... + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + logger.debug("skip signal test for tile %d, RCUs turned off" % tile.nr) + + ctrl_2_subband = None + + for ctrl in ('128,', '253,', '2,'): + if not self.db.check_end_time(duration=80.0): + logger.warning("check stopped, end time reached") + return + + ctrl_nr = -1 + if ctrl == '128,': + ctrl_nr = 0 + elif ctrl == '253,': + ctrl_nr = 1 + + logger.debug("HBA signal test, ctrl word %s" % (ctrl[:-1])) + + delay_str = (ctrl * 16)[:-1] + rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + + check_for_valid_retries = 3 + record_time = 2 + while check_for_valid_retries > 0: + self.record_data(rec_time=record_time, new_data=True) + if ctrl_nr == -1: + parset.parset['rf']['subbands'] = str(ctrl_2_subband) + + logger.debug("subband=%s" % parset.parset['rf']['subbands']) + test_info_x, signal_info_x = check_rf_power(data=self.antenna_data, band=mode_to_band(mode), + pol='X', parset=parset) + test_info_y, signal_info_y = check_rf_power(data=self.antenna_data, band=mode_to_band(mode), + pol='Y', parset=parset) + if ctrl_nr > -1: + if test_info_x['valid'] and test_info_y['valid']: + check_for_valid_retries = 0 + else: + logger.warning("HBA, No valid test signal, try again") + check_for_valid_retries -= 1 + record_time = 30 + else: + check_for_valid_retries = 0 + + logger.debug("X data: control-word=%s, test subband=%d, median val=%5.3f" % ( + ctrl[:-1], test_info_x['subband'], test_info_x['test_val'])) + + logger.debug("Y data: control-word=%s, test subband=%d, median val=%5.3f" % ( + ctrl[:-1], test_info_y['subband'], test_info_y['test_val'])) + + if ctrl_nr > -1: + ctrl_2_subband = test_info_x['subband'] + self.hba.rf_ref_signal_x[ctrl_nr] = test_info_x['test_val'] + self.hba.rf_ref_signal_y[ctrl_nr] = test_info_y['test_val'] + self.hba.rf_test_subband_x[ctrl_nr] = test_info_x['subband'] + self.hba.rf_test_subband_y[ctrl_nr] = test_info_y['subband'] + + if test_info_x['valid'] and test_info_y['valid']: + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + continue + + if str(tile.x.rcu) in signal_info_x: + tile.x.test_signal[ctrl_nr] = signal_info_x[str(tile.x.rcu)]['value'] + + if signal_info_x[str(tile.x.rcu)]['status'] == 'no_signal': + tile.x.no_signal = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'no_power': + tile.no_power = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'low': + tile.x.too_low = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'high': + tile.x.too_high = 1 + + if str(tile.y.rcu) in signal_info_y: + tile.y.test_signal[ctrl_nr] = signal_info_y[str(tile.y.rcu)]['value'] + + if signal_info_y[str(tile.y.rcu)]['status'] == 'no_signal': + tile.y.no_signal = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'no_power': + tile.no_power = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'low': + tile.y.too_low = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'high': + tile.y.too_high = 1 + + if str(tile.x.rcu) in signal_info_x and str(tile.y.rcu) in signal_info_y: + if signal_info_x[str(tile.x.rcu)]['status'] != 'normal' or \ + signal_info_y[str(tile.y.rcu)]['status'] != 'normal': + logger.info("HBA Tile=%d: control-word=%s X=%3.1fdB(%s) Y=%3.1fdB(%s)" % ( + tile.nr, + ctrl[:-1], + signal_info_x[str(tile.x.rcu)]['value'], + signal_info_x[str(tile.x.rcu)]['status'], + signal_info_y[str(tile.y.rcu)]['value'], + signal_info_y[str(tile.y.rcu)]['status'])) + else: + logger.warning("HBA, No valid test signal") + self.hba.rf_signal_to_low = 1 + else: + # TODO: not valid, so no values, change spectrum_checks.py + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + continue + if str(tile.x.rcu) in signal_info_x: + if signal_info_x[str(tile.x.rcu)]['status'] in ('high',): + logger.warning("Tile %d rcu %d not switched off" % (tile.nr, tile.x.rcu)) + if str(tile.y.rcu) in signal_info_y: + if signal_info_y[str(tile.y.rcu)]['status'] in ('high',): + logger.warning("Tile %d rcu %d not switched off" % (tile.nr, tile.y.rcu)) + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.signal_check_done = 1 + self.db.add_test_done('S%d' % mode) + logger.info("=== Done HBA signal test ===") + return + + # Next tests are element based + # + # 8bit control word + # + # bit-7 RF on/off 1 = on + # bit-6 delay 1 = 8 ns + # bit-5 delay 1 = 4 ns + # bit-4 delay 1 = 2 ns + # bit-3 delay 1 = 1 ns + # bit-2 delay 1 = 0.5 ns + # bit-1 LNA on/off 1 = off + # bit-0 LED on/off 1 = on + # + # control word = 0 (signal - 30 db) + # control word = 2 (signal - 40 db) + # + def check_elements(self, mode, record_time, parset): + + logger.info("=== Start HBA element based tests ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + self.set_mode(mode) + + n_rcus_off = 0 + for ctrl in ('128', '253'): + ctrl_nr = -1 + if ctrl == '128': + ctrl_nr = 0 + elif ctrl == '253': + ctrl_nr = 1 + + parset.parset['ctrl-word'] = ctrl + + #logger.info(" check elements with ctrlword %s" % (ctrl)) + for elem in range(self.hba.tile[0].nr_elements): + logger.info(" check elements %d with control-word %s" % ((elem + 1), ctrl)) + + if not self.db.check_end_time(duration=45.0): + logger.warning("check stopped, end time reached") + return + + if n_rcus_off > 0: + rsp_rcu_mode(mode=mode, rcus=self.hba.select_list()) + n_rcus_off = 0 + for tile in self.hba.tile: + if tile.element[elem].no_modem or tile.element[elem].modem_error: + self.turn_off_tile(tile.nr) + n_rcus_off += 1 + logger.debug("skip tile %d, modem error" % tile.nr) + + delay_str = ('2,' * elem + ctrl + ',' + '2,' * 15)[:33] + rsp_hba_delay(delay=delay_str, rcus=self.hba.select_list()) + + clean = False + while not clean: + if not self.db.check_end_time(duration=(record_time + 45.0)): + logger.warning("check stopped, end time reached") + return + + self.record_data(rec_time=record_time, new_data=True) + + clean, n_off = self.check_oscillation_elements(elem, parset) + n_rcus_off += n_off + if n_off > 0: + continue + n_off = self.check_spurious_elements(elem, parset) + n_rcus_off += n_off + if n_off > 0: + continue + self.check_noise_elements(elem, parset) + self.check_rf_power_elements(elem, ctrl_nr, parset) + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.hba.element_check_done = 1 + self.db.add_test_done('E%d' % mode) + logger.info("=== Done HBA element tests ===") + return + + # check for oscillating tiles and turn off RCU + # stop one RCU each run + # elem counts from 0..15 (for user output use 1..16) + def check_oscillation_elements(self, elem, parset): + logger.info("--- oscillation test --") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + clean = True + n_rcus_off = 0 + # result is a sorted list on maxvalue + result = check_for_oscillation(data=self.antenna_data, band=mode_to_band(self.db.rcumode), pol='XY', + parset=parset) + if len(result) > 1: + clean = False + rcu, peaks_sum, n_peaks, rcu_low = sorted(result[1:], reverse=True)[0] # result[1] + tile = rcu / 2 + if self.hba.tile[tile].element[elem].no_modem or self.hba.tile[tile].element[elem].modem_error: + return True, 0 + tile_polarity = rcu % 2 + logger.info("%s RCU %d Tile %d Element %d Oscillation sum=%3.1f peaks=%d, low=%3.1f" % ( + parset.as_string('ctrl-word'), rcu, tile, elem + 1, peaks_sum, n_peaks, rcu_low)) + self.turn_off_tile(tile) + n_rcus_off += 1 + if tile_polarity == 0: + self.hba.tile[tile].element[elem].x.osc = 1 + else: + self.hba.tile[tile].element[elem].y.osc = 1 + return clean, n_rcus_off + + def check_spurious_elements(self, elem, parset): + logger.info("--- spurious test ---") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + n_rcus_off = 0 + # result is a sorted list on maxvalue + result = check_for_spurious(data=self.antenna_data, band=mode_to_band(self.db.rcumode), pol='XY', + parset=parset) + for rcu in result: + tile = rcu / 2 + tile_polarity = rcu % 2 + logger.info("%s RCU %d Tile %d Element %d pol %d Spurious" % (parset.as_string('ctrl-word'), rcu, tile, elem + 1, tile_polarity)) + self.turn_off_tile(tile) + n_rcus_off += 1 + if tile_polarity == 0: + self.hba.tile[tile].element[elem].x.spurious = 1 + else: + self.hba.tile[tile].element[elem].y.spurious = 1 + return n_rcus_off + + def check_noise_elements(self, elem, parset): + logger.info("--- noise test ---") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + # result is a sorted list on maxvalue + low_noise, high_noise, jitter = check_for_noise(data=self.antenna_data, band=mode_to_band(self.db.rcumode), + pol='XY', parset=parset) + + for n in low_noise: + rcu, val, bad_secs, ref, diff = n + tile = rcu / 2 + logger.info("%s RCU %d Tile %d Element %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" % ( + parset.as_string('ctrl-word'), rcu, tile, elem + 1, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + if rcu % 2 == 0: + elem_polarity = self.hba.tile[tile].element[elem].x + else: + elem_polarity = self.hba.tile[tile].element[elem].y + + elem_polarity.low_seconds += self.antenna_data.seconds() + elem_polarity.low_bad_seconds += bad_secs + if val < elem_polarity.low_val: + elem_polarity.low_noise = 1 + elem_polarity.low_val = val + elem_polarity.low_ref = ref + elem_polarity.low_diff = diff + + for n in high_noise: + rcu, val, bad_secs, ref, diff = n + tile = rcu / 2 + logger.info("%s RCU %d Tile %d Element %d High-Noise value=%3.1f bad=%d(%d) ref=%3.1f diff=%3.1f" % ( + parset.as_string('ctrl-word'), rcu, tile, elem + 1, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + if rcu % 2 == 0: + elem_polarity = self.hba.tile[tile].element[elem].x + else: + elem_polarity = self.hba.tile[tile].element[elem].y + + elem_polarity.high_seconds += self.antenna_data.seconds() + elem_polarity.high_bad_seconds += bad_secs + if val > elem_polarity.high_val: + elem_polarity.high_noise = 1 + elem_polarity.high_val = val + elem_polarity.high_ref = ref + elem_polarity.high_diff = diff + + for n in jitter: + rcu, val, ref, bad_secs = n + tile = rcu / 2 + logger.info("%s RCU %d Tile %d Element %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" % ( + parset.as_string('ctrl-word'), rcu, tile, elem + 1, val, ref)) + + if rcu % 2 == 0: + elem_polarity = self.hba.tile[tile].element[elem].x + else: + elem_polarity = self.hba.tile[tile].element[elem].y + + elem_polarity.jitter_seconds += self.antenna_data.seconds() + elem_polarity.jitter_bad_seconds += bad_secs + if val > elem_polarity.jitter_val: + elem_polarity.jitter = 1 + elem_polarity.jitter_val = val + elem_polarity.jitter_ref = ref + return + + def check_rf_power_elements(self, elem, ctrl_nr, parset): + + logger.info("--- RF test ---") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + test_info_x, signal_info_x = check_rf_power(data=self.antenna_data, band=mode_to_band(self.db.rcumode), + pol='X', parset=parset) + logger.debug("X data: test subband=%d, median val=%5.3f" % (test_info_x['subband'], + test_info_x['test_val'])) + + test_info_y, signal_info_y = check_rf_power(data=self.antenna_data, band=mode_to_band(self.db.rcumode), + pol='Y', parset=parset) + logger.debug("Y data: test subband=%d, median val=%5.3f" % (test_info_y['subband'], + test_info_y['test_val'])) + + if test_info_x['valid'] and test_info_y['valid']: + for tile in self.hba.tile: + if tile.x.rcu_off or tile.y.rcu_off: + continue + + tile.element[elem].x.rf_ref_signal[ctrl_nr] = test_info_x['test_val'] + tile.element[elem].y.rf_ref_signal[ctrl_nr] = test_info_y['test_val'] + tile.element[elem].x.rf_test_subband[ctrl_nr] = test_info_x['subband'] + tile.element[elem].y.rf_test_subband[ctrl_nr] = test_info_y['subband'] + + if str(tile.x.rcu) in signal_info_x: + tile.element[elem].x.test_signal[ctrl_nr] = signal_info_x[str(tile.x.rcu)]['value'] + if signal_info_x[str(tile.x.rcu)]['status'] == 'no_signal': + tile.element[elem].x.no_signal = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'no_power': + tile.element[elem].no_power = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'low': + tile.element[elem].x.too_low = 1 + elif signal_info_x[str(tile.x.rcu)]['status'] == 'high': + tile.element[elem].x.too_high = 1 + + if str(tile.y.rcu) in signal_info_y: + tile.element[elem].y.test_signal[ctrl_nr] = signal_info_y[str(tile.y.rcu)]['value'] + if signal_info_y[str(tile.y.rcu)]['status'] == 'no_signal': + tile.element[elem].y.no_signal = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'no_power': + tile.element[elem].no_power = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'low': + tile.element[elem].y.too_low = 1 + elif signal_info_y[str(tile.y.rcu)]['status'] == 'high': + tile.element[elem].y.too_high = 1 + + if str(tile.x.rcu) in signal_info_x and str(tile.y.rcu) in signal_info_y: + if signal_info_x[str(tile.x.rcu)]['status'] != 'normal' or \ + signal_info_y[str(tile.y.rcu)]['status'] != 'normal': + logger.info("%s HBA Tile=%d Elem=%d: X=%3.1fdB(%s) Y=%3.1fdB(%s)" % ( + parset.as_string('ctrl-word'), + tile.nr, + elem + 1, + signal_info_x[str(tile.x.rcu)]['value'], + signal_info_x[str(tile.x.rcu)]['status'], + signal_info_y[str(tile.y.rcu)]['value'], + signal_info_y[str(tile.y.rcu)]['status'])) + else: + logger.warning("HBA Elem=%d, No valid test signal" % (elem + 1)) + # self.hba.rf_signal_to_low = 1 + + return diff --git a/LCU/checkhardware/checkhardware_lib/lba.py b/LCU/checkhardware/checkhardware_lib/lba.py new file mode 100644 index 0000000000000000000000000000000000000000..feab5aa504d2eb4d33a1374685da535a76eae9db --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/lba.py @@ -0,0 +1,474 @@ +import logging +from data import AntennaData +from spectrum_checks import * +from lofar import * + +logger = logging.getLogger('main.lba') +logger.debug("starting lba logger") + +# class for testing LBA antennas +class LBA(object): + def __init__(self, db, lba): + self.db = db + self.lba = lba + self.antenna_data = AntennaData({'n_rcus': self.lba.nr_antennas * 2, 'data-dir': data_dir()}) + self.db.rcus_changed = False + + # Average normal value = 150.000.000 (81.76 dBm) -3dB +3dB + # LOW/HIGH LIMIT is used for calculating mean value + self.lowLimit = -3.0 # dB + self.highLimit = 3.0 # dB + + # MEAN LIMIT is used to check if mean of all antennas is ok + self.meanLimit = 66.0 # dB + + def reset(self): + self.db.rcus_changed = False + + def turn_off_ant(self, ant_nr): + ant = self.lba.ant[ant_nr] + ant.x.rcu_off = 1 + ant.y.rcu_off = 1 + logger.info("turned off antenna %d RCU(%d,%d)" % (ant.nr_pvss, ant.x.rcu, ant.y.rcu)) + rspctl("--rcumode=0 --select=%d,%d" % (ant.x.rcu, ant.y.rcu), wait=2.0) + rspctl("--rcuenable=0 --select=%d,%d" % (ant.x.rcu, ant.y.rcu), wait=2.0) + self.db.rcus_changed = True + return + + def set_mode(self, mode): + if self.db.rcumode != mode: + self.db.rcumode = mode + turn_off_rcus() + turn_on_rcus(mode=mode, rcus=self.lba.select_list()) + self.lba.reset_rcu_state() + + def record_data(self, rec_time, new_data=False): + if new_data or self.db.rcus_changed or self.antenna_data.seconds() < rec_time: + self.db.rcus_changed = False + logger.debug('record info changed') + self.antenna_data.collect(n_seconds=rec_time) + for ant in self.lba.ant: + if ant.x.rcu_off or ant.y.rcu_off: + self.antenna_data.mask_rcu([ant.x.rcu, ant.y.rcu]) + + # check for oscillating tiles and turn off RCU + # stop one RCU each run + def check_oscillation(self, mode, parset): + logger.info("=== Start %s oscillation test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=28.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + band = mode_to_band(mode) + + clean = False + while not clean: + if not self.db.check_end_time(duration=18.0): + logger.warning("check stopped, end time reached") + return + + clean = True + self.record_data(rec_time=3, new_data=True) + + + for pol in ('X', 'Y'): + # result is a sorted list on maxvalue + result = check_for_oscillation(data=self.antenna_data, band=band, pol=pol, parset=parset) + if len(result) > 1: + clean = False + rcu, peaks_sum, n_peaks, ant_low = sorted(result[1:], reverse=True)[0] # result[1] + ant = rcu / 2 + logger.info("RCU %d LBA %d Oscillation sum=%3.1f peaks=%d low=%3.1fdB" % ( + rcu, self.lba.ant[ant].nr_pvss, peaks_sum, n_peaks, ant_low)) + self.turn_off_ant(ant) + if pol == 'X': + self.lba.ant[ant].x.osc = 1 + else: + self.lba.ant[ant].y.osc = 1 + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.oscillation_check_done = 1 + self.db.add_test_done('O%d' % mode) + logger.info("=== Done %s oscillation test ===" % self.lba.label) + return + + def check_noise(self, mode, record_time, parset): + logger.info("=== Start %s noise test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=(record_time + 100.0)): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + band = mode_to_band(mode) + + for ant in self.lba.ant: + if ant.x.rcu_off or ant.y.rcu_off: + logger.info("skip low-noise test for antenna %d, RCUs turned off" % ant.nr) + + self.record_data(rec_time=record_time) + + # result is a sorted list on maxvalue + low_noise, high_noise, jitter = check_for_noise(data=self.antenna_data, band=band, pol='XY', parset=parset) + + for n in low_noise: + rcu, val, bad_secs, ref, diff = n + ant = rcu / 2 + if self.lba.ant[ant].x.rcu_off or self.lba.ant[ant].y.rcu_off: + continue + # self.turnOffAnt(ant) + logger.info("RCU %d Ant %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" % ( + rcu, self.lba.ant[ant].nr_pvss, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + antenna = self.lba.ant[ant].x + else: + antenna = self.lba.ant[ant].y + + antenna.low_seconds += self.antenna_data.seconds() + antenna.low_bad_seconds += bad_secs + if val < self.lba.ant[ant].x.low_val: + antenna.low_noise = 1 + antenna.low_val = val + antenna.low_ref = ref + antenna.low_diff = diff + + for n in high_noise: + rcu, val, bad_secs, ref, diff = n + ant = rcu / 2 + # self.turnOffAnt(ant) + logger.info("RCU %d Ant %d High-Noise value=%3.1f bad=%d(%d) ref=%3.1f diff=%3.1f" % ( + rcu, self.lba.ant[ant].nr_pvss, val, bad_secs, self.antenna_data.seconds(), ref, diff)) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + antenna = self.lba.ant[ant].x + else: + antenna = self.lba.ant[ant].y + + antenna.high_seconds += self.antenna_data.seconds() + antenna.high_bad_seconds += bad_secs + if val > self.lba.ant[ant].x.high_val: + antenna.high_noise = 1 + antenna.high_val = val + antenna.high_ref = ref + antenna.high_diff = diff + + for n in jitter: + rcu, val, ref, bad_secs = n + ant = rcu / 2 + logger.info("RCU %d Ant %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" % ( + rcu, self.lba.ant[ant].nr_pvss, val, ref)) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + antenna = self.lba.ant[ant].x + else: + antenna = self.lba.ant[ant].y + + antenna.jitter_seconds += self.antenna_data.seconds() + antenna.jitter_bad_seconds += bad_secs + if val > antenna.jitter_val: + antenna.jitter = 1 + antenna.jitter_val = val + antenna.jitter_ref = ref + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.noise_check_done = 1 + self.db.add_test_done('NS%d=%d' % (mode, record_time)) + logger.info("=== Done %s noise test ===" % self.lba.label) + return + + def check_spurious(self, mode, parset): + logger.info("=== Start %s spurious test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=12.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + + self.record_data(rec_time=3) + + # result is a sorted list on maxvalue + result = check_for_spurious(data=self.antenna_data, band=mode_to_band(mode), pol='XY', parset=parset) + for rcu in result: + ant = rcu / 2 + # self. turnOffAnt(ant) + logger.info("RCU %d Ant %d pol %s Spurious" % ( + rcu, self.lba.ant[ant].nr_pvss, self.antenna_data.polarity(rcu))) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + self.lba.ant[ant].x.spurious = 1 + else: + self.lba.ant[ant].y.spurious = 1 + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.spurious_check_done = 1 + self.db.add_test_done('SP%d' % mode) + logger.info("=== Done %s spurious test ===" % self.lba.label) + return + + def check_short(self, mode, parset): + logger.info("=== Start %s Short test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=15.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + band = mode_to_band(mode) + self.record_data(rec_time=3) + + # search for shorted cable (input), mean signal all subbands between 55 and 61 dB + logger.debug("Check Short") + short = check_for_short(data=self.antenna_data, band=band, parset=parset) + for i in short: + rcu, mean_val = i + ant = rcu / 2 + + logger.info("%s %2d RCU %3d Short, mean value band=%5.1fdB" % ( + self.lba.label, self.lba.ant[ant].nr_pvss, rcu, mean_val)) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + self.lba.ant[ant].x.short = 1 + self.lba.ant[ant].x.short_val = mean_val + else: + self.lba.ant[ant].y.short = 1 + self.lba.ant[ant].y.short_val = mean_val + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.short_check_done = 1 + self.db.add_test_done('SH%d' % mode) + logger.info("=== Done %s Short test ===" % self.lba.label) + return + + def check_flat(self, mode, parset): + logger.info("=== Start %s Flat test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=15.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + band = mode_to_band(mode) + + self.record_data(rec_time=3) + + # search for flatliners, mean signal all subbands between 63 and 65 dB + logger.debug("Check Flat") + flat = check_for_flat(data=self.antenna_data, band=band, parset=parset) + for i in flat: + rcu, mean_val = i + ant = rcu / 2 + + logger.info("%s %2d RCU %3d Flat, mean value band=%5.1fdB" % ( + self.lba.label, + self.lba.ant[ant].nr_pvss, + rcu, + mean_val)) + + self.antenna_data.mask_rcu(rcu) + if self.antenna_data.polarity(rcu) == self.antenna_data.XYPOL: + self.lba.ant[ant].x.flat = 1 + self.lba.ant[ant].x.flat_val = mean_val + else: + self.lba.ant[ant].y.flat = 1 + self.lba.ant[ant].y.flat_val = mean_val + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.flat_check_done = 1 + self.db.add_test_done('F%d' % mode) + logger.info("=== Done %s Flat test ===" % self.lba.label) + return + + def check_down(self, mode, parset): + logger.info("=== Start %s Down test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=15.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + self.record_data(rec_time=3) + + # mark lba as down if top of band is lower than normal and top is shifted more than 10 subbands to left or right + logger.debug("Check Down") + down, shifted = check_for_down(data=self.antenna_data, band=mode_to_band(mode), parset=parset) + #logger.debug("down_info=%s" % str(down)) + + for rcu, max_sb, max_val, max_bw, band_pwr in down: + if rcu == 'Xref': + x_ref_max_sb = max_sb + x_ref_max_val = max_val + x_ref_max_bw = max_bw + x_ref_band_pwr = band_pwr + logger.info("down test X rcus's median values: sb=%d, pwr=%3.1fdB, bw=%d, band-pwr=%3.1fdB" % ( + x_ref_max_sb, x_ref_max_val, x_ref_max_bw, x_ref_band_pwr)) + continue + if rcu == 'Yref': + y_ref_max_sb = max_sb + y_ref_max_val = max_val + y_ref_max_bw = max_bw + y_ref_band_pwr = band_pwr + logger.info("down test Y rcu's median values: sb=%d, pwr=%3.1fdB, bw=%d, band-pwr=%3.1fdB" % ( + y_ref_max_sb, y_ref_max_val, y_ref_max_bw, y_ref_band_pwr)) + continue + + max_offset = 292 - max_sb + ant = rcu / 2 + + if self.lba.ant[ant].x.flat or self.lba.ant[ant].x.short or \ + self.lba.ant[ant].y.flat or self.lba.ant[ant].y.short: + continue + if self.antenna_data.polarity(rcu) == self.antenna_data.XPOL: + self.lba.ant[ant].x.down_pwr = max_val + self.lba.ant[ant].x.down_offset = max_offset + self.lba.ant[ant].down = 1 + else: + self.lba.ant[ant].y.down_pwr = max_val + self.lba.ant[ant].y.down_offset = max_offset + self.lba.ant[ant].down = 1 + + self.antenna_data.mask_rcu([self.lba.ant[ant].x.rcu, self.lba.ant[ant].y.rcu]) + + for a in xrange(self.lba.nr_antennas): + if self.lba.ant[a].down: + logger.info("%s %2d RCU %3d/%3d Down, Xoffset=%d Yoffset=%d" % ( + self.lba.label, self.lba.ant[a].nr_pvss, + self.lba.ant[a].x.rcu, self.lba.ant[a].y.rcu, + self.lba.ant[a].x.down_offset, self.lba.ant[a].y.down_offset)) + + for i in shifted: + rcu, max_sb, mean_max_sb = i + ant = rcu / 2 + logger.info("%s %2d RCU %3d shifted top on sb=%d, normal=sb%d" % ( + self.lba.label, self.lba.ant[ant].nr_pvss, rcu, max_sb, mean_max_sb)) + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.down_check_done = 1 + self.db.add_test_done('D%d' % mode) + logger.info("=== Done %s Down test ===" % self.lba.label) + return + + def check_rf_power(self, mode, parset): + logger.info("=== Start %s RF test ===" % self.lba.label) + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + if not self.db.check_end_time(duration=15.0): + logger.warning("check stopped, end time reached") + return + + self.set_mode(mode) + self.record_data(rec_time=2) + + test_info_x, signal_info_x = check_rf_power(data=self.antenna_data, band=mode_to_band(mode), + pol='X', parset=parset) + logger.debug("X data: test subband=%d, median val=%5.3f" % (test_info_x['subband'], test_info_x['test_val'])) + + test_info_y, signal_info_y = check_rf_power(data=self.antenna_data, band=mode_to_band(mode), + pol='Y', parset=parset) + logger.debug("Y data: test subband=%d, median val=%5.3f" % (test_info_y['subband'], test_info_y['test_val'])) + + # logger.debug("signal_info_x=%s" % str(signal_info_x)) + # logger.debug("signal_info_y=%s" % str(signal_info_y)) + + self.lba.rf_ref_signal_y = test_info_y['test_val'] + self.lba.rf_ref_signal_x = test_info_x['test_val'] + self.lba.rf_test_subband_x = test_info_x['subband'] + self.lba.rf_test_subband_y = test_info_y['subband'] + + rcu_list = self.antenna_data.rcus(mode_to_band(mode), 'XY') + if test_info_x['valid'] and test_info_y['valid']: + for ant in self.lba.ant: + if ant.x.rcu_off or ant.y.rcu_off: + continue + + if str(ant.x.rcu) in signal_info_x: + ant.x.test_signal = signal_info_x[str(ant.x.rcu)]['value'] + if signal_info_x[str(ant.x.rcu)]['status'] == 'no_signal': + ant.x.no_signal = 1 + elif signal_info_x[str(ant.x.rcu)]['status'] == 'no_power': + ant.no_power = 1 + elif signal_info_x[str(ant.x.rcu)]['status'] == 'low': + ant.x.too_low = 1 + elif signal_info_x[str(ant.x.rcu)]['status'] == 'high': + ant.x.too_high = 1 + + if str(ant.y.rcu) in signal_info_y: + ant.y.test_signal = signal_info_y[str(ant.y.rcu)]['value'] + if signal_info_y[str(ant.y.rcu)]['status'] == 'no_signal': + ant.y.no_signal = 1 + elif signal_info_y[str(ant.y.rcu)]['status'] == 'no_power': + ant.no_power = 1 + elif signal_info_y[str(ant.y.rcu)]['status'] == 'low': + ant.y.too_low = 1 + elif signal_info_y[str(ant.y.rcu)]['status'] == 'high': + ant.y.too_high = 1 + + if str(ant.x.rcu) in signal_info_x and str(ant.y.rcu) in signal_info_y: + if signal_info_x[str(ant.x.rcu)]['status'] != 'normal' or \ + signal_info_y[str(ant.y.rcu)]['status'] != 'normal': + logger.info("LBA Ant=%d: X=%3.1fdB(%s) Y=%3.1fdB(%s)" % ( + ant.nr, + signal_info_x[str(ant.x.rcu)]['value'], + signal_info_x[str(ant.x.rcu)]['status'], + signal_info_y[str(ant.y.rcu)]['value'], + signal_info_y[str(ant.y.rcu)]['status'])) + else: + logger.warning("LBA, No valid test signal") + self.lba.rf_signal_to_low = 1 + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return + + self.lba.signal_check_done = 1 + self.db.add_test_done('S%d' % mode) + logger.info("=== Done %s RF test ===" % self.lba.label) + return + + # end of cLBA class diff --git a/LCU/checkhardware/checkhardware_lib/lofar.py b/LCU/checkhardware/checkhardware_lib/lofar.py new file mode 100644 index 0000000000000000000000000000000000000000..6a05a5294c98b961a797c076eaac956a2fa8294b --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/lofar.py @@ -0,0 +1,608 @@ +# lofar_lib + +import os +import sys +import time +import logging +import socket +import struct +import string +from general import * + +os.umask(001) +lofar_version = '0514' +testmode = False +#testmode = True + + +CoreStations = ('CS001C', 'CS002C', 'CS003C', 'CS004C', 'CS005C', 'CS006C', 'CS007C', 'CS011C', 'CS013C', 'CS017C', + 'CS021C', 'CS024C', 'CS026C', 'CS028C', 'CS030C', 'CS031C', 'CS032C', 'CS101C', 'CS103C', 'CS201C', + 'CS301C', 'CS302C', 'CS401C', 'CS501C') + +RemoteStations = ('RS106C', 'RS205C', 'RS208C', 'RS210C', 'RS305C', 'RS306C', 'RS307C', 'RS310C', 'RS406C', 'RS407C', + 'RS409C', 'RS503C', 'RS508C', 'RS509C') + +InternationalStations = ('DE601C', 'DE602C', 'DE603C', 'DE604C', 'DE605C', 'DE609C', 'FR606C', 'SE607C', + 'UK608C', 'PL610C', 'PL611C', 'PL612C') + +StationType = {'CS': 1, 'RS': 2, 'IS': 3} + +logger = logging.getLogger('main.lofar') +logger.debug("starting lofar logger") + +active_delay_str = ('555,' * 16)[:-1] + + +def activate_test_mode(): + global testmode + testmode = True + + +def is_test_mode_active(): + return testmode + + +def init_lofar_lib(): + if not os.access(data_dir(), os.F_OK): + os.mkdir(data_dir()) + + +def data_dir(): + return r'/localhome/stationtest/sb_data' + + +# remove all *.dat +def remove_all_data_files(): + if not testmode: + if os.access(data_dir(), os.F_OK): + files = os.listdir(data_dir()) + # print files + for f in files: + if f[-3:] == 'dat' or f[-3:] == 'nfo': + os.remove(os.path.join(data_dir(), f)) + + +# return station type +def get_station_type(StID): + if StID in CoreStations: + return StationType['CS'] + if StID in RemoteStations: + return StationType['RS'] + if StID in InternationalStations: + return StationType['IS'] + + +# read from RemoteStation.conf file number of RSP and TB Boards +""" +# +# THIS FILE IS GENERATED, DO NOT MODIFY IT. +# +# RemoteStation.conf for CS002 +# +# Describes the amount of available hardware on the station. +# + +RS.STATION_ID = 2 +RS.N_RSPBOARDS = 12 +RS.N_TBBOARDS = 6 +RS.N_LBAS = 96 +RS.N_HBAS = 48 +RS.HBA_SPLIT = Yes +RS.WIDE_LBAS = Yes +""" + + +def read_station_config(): + f = open('/opt/lofar/etc/RemoteStation.conf', 'r') + lines = f.readlines() + f.close() + + st_id = nrsp = ntbb = nlba = nlbl = nlbh = nhba = hba_split = 0 + + for line in lines: + if (line[0] == '#') or (len(line) < 2): + continue + key, val = line.split('=') + key = key.strip() + val = val.strip() + if key == "RS.STATION_ID": + st_id = int(val) + continue + if key == "RS.N_RSPBOARDS": + nrsp = int(val) + continue + if key == "RS.N_TBBOARDS": + ntbb = int(val) + continue + if key == "RS.N_LBAS": + nlba = int(val) + if nlba == nrsp * 8: + nlbl = nlba / 2 + nlbh = nlba / 2 + else: + nlbl = 0 + nlbh = nlba + continue + if key == "RS.N_HBAS": + nhba = int(val) + continue + if key == "RS.HBA_SPLIT": + if string.upper(val) == "YES": + hba_split = 1 + continue + return st_id, nrsp, ntbb, nlbl, nlbh, nhba, hba_split + + +# [lofarsys@RS306C stationtest]$ swlevel 2 +# Going to level 2 +# Starting RSPDriver +# Loading image 4 on RSPboard 0 ... +# Loading image 4 on RSPboard 1 ... +# Loading image 4 on RSPboard 2 ... +# Loading image 4 on RSPboard 3 ... +# Loading image 4 on RSPboard 4 ... +# Loading image 4 on RSPboard 5 ... +# Loading image 4 on RSPboard 6 ... +# Loading image 4 on RSPboard 7 ... +# RSPboard 8: Error requesting active firmware version (communication error) +# Loading image 4 on RSPboard 9 ... +# Loading image 4 on RSPboard 10 ... +# Loading image 4 on RSPboard 11 ... +# One or more boards have a communication problem; try reset the 48V +# root 21470 1 1 10:41 pts/2 00:00:00 /opt/lofar/bin/RSPDriver +# Starting TBBDriver +# root 21492 1 0 10:41 pts/2 00:00:00 /opt/lofar/bin/TBBDriver +# +# Status of all software level: +# 1 : PVSS00pmon 16177 +# 1 : SoftwareMonitor 16227 +# 1 : LogProcessor 16248 +# 1 : ServiceBroker 16278 +# 1 : SASGateway 16299 +# --- +# 2 : RSPDriver 21470 +# 2 : TBBDriver 21492 +# --- +# 3 : CalServer DOWN +# 3 : BeamServer DOWN +# --- +# 4 : HardwareMonitor DOWN +# --- +# 5 : SHMInfoServer DOWN +# --- +# 6 : CTStartDaemon DOWN +# 6 : StationControl DOWN +# 6 : ClockControl DOWN +# 6 : CalibrationControl DOWN +# 6 : BeamControl DOWN +# 6 : TBBControl DOWN +# --- + +def swlevel(level=None): + # level = None + _level = level + board_errors = list() + if level is not None: + if _level < 0: + _level *= -1 + answer = run_cmd('swlevel %d' % _level) + else: + answer = run_cmd('swlevel') + + #print answer + current_level = 0 + for line in answer.splitlines(): + if line.find("Going to level") > -1: + current_level = int(line.split()[-1]) + + elif line.find("Currently set level") > -1: + current_level = int(line.strip().split()[-1]) + if current_level < 0: + logger.warning("Current swlevel is %d" % current_level) + if line.find("Error requesting active firmware version") > -1: + endpos = line.find(":") + board_errors.append(int(line[:endpos].split()[1])) + logger.warning(line) + logger.info("current swlevel = %s" % current_level) + return current_level, board_errors + + +def reset_48_volt(): + logger.info("Try to reset 48V power") + ec_name = socket.gethostname()[:-1] + "ec" + ec_ip = socket.gethostbyname(ec_name) + logger.debug("EC to connect = %s" % ec_ip) + + try: + sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except socket.error: + # sck.close() + return + except: + raise + + try: + sck.settimeout(4.0) + sck.connect((ec_ip, 10000)) + time.sleep(0.5) + cmd = struct.pack('hhh', 22, 0, 0) + logger.debug("send cmd") + sck.send(cmd) + sck.settimeout(4.0) + logger.debug("recv cmd") + data = sck.recv(6) + sck.close() + logger.debug("reset done") + except socket.error: + logger.error("ec socket connect error") + sck.close() + except: + raise + + + + +# Run rspctl command with given args and return response +def rspctl(args='', wait=0.0): + if str(args) != '': + logger.debug("rspctl %s" % str(args)) + response = run_cmd('rspctl %s' % str(args)) + if not testmode and wait > 0.0: + time.sleep(wait) + return response + return 'No args given' + + +# Run tbbctl command with given args and return response +def tbbctl(args=''): + if str(args) != '': + logger.debug("tbbctl %s" % str(args)) + return run_cmd('tbbctl %s' % str(args)) + return 'No args given' + + +def check_active_tbbdriver(): + answer = run_cmd('swlevel').strip().splitlines() + for line in answer: + if line.find('TBBDriver') > -1: + if line.find('DOWN') != -1: + return False + return True + + +# wait until all boards have a working image loaded +# returns 1 if ready or 0 if timed_out +def wait_tbb_ready(n_boards=6): + board_active = [True] * n_boards + timeout = 30 + logger.info("wait for working TBB boards ") + while timeout > 0: + answer = tbbctl('--version') + # print answer + if answer.find('TBBDriver is NOT responding') > 0: + if timeout < 10: + logger.warning("TBBDriver is NOT responding, try again in every 5 seconds") + time.sleep(5.0) + timeout -= 5 + continue + + # check if image_nr > 0 for all boards + if answer.count('V') == (n_boards * 4): + logger.debug("All boards in working image") + return True, board_active + + reset_list = [] + board_not_ready = False + for line in answer.splitlines(): + if line.count('V') == 4: + board_nr = int(line.split()[0]) + board_active[board_nr] = True + if 'boards not active' in line: + board_nr = int(line.split()[0]) + board_active[board_nr] = False + if 'mpi time-out' in line: + board_nr = line.split()[0] + reset_list.append(board_nr) + if 'board not ready' in line: + board_not_ready = True + + if board_not_ready: + timeout += 5.0 + + if len(reset_list) > 0: + tbbctl('--reset --select=%s' % ','.join(reset_list)) + reset_list = [] + timeout += 35.0 + time.sleep(35.0) + + time.sleep(1.0) + timeout -= 1 + + logger.warning("Not all TB boards in working image") + return False, board_active + + +def check_active_rspdriver(): + answer = run_cmd('swlevel').strip().splitlines() + for line in answer: + if line.find('RSPDriver') > -1: + if line.find('DOWN') != -1: + return False + return True + + +# wait until all boards have a working image loaded +# returns 1 if ready or 0 if timed_out +# +# [lofarsys@RS306C ~]$ rspctl --version +# RSP[ 0] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 1] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 2] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 3] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 4] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 5] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 6] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 7] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 8] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[ 9] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[10] RSP version = 0, BP version = 0.0, AP version = 0.0 +# RSP[11] RSP version = 0, BP version = 0.0, AP version = 0.0 + +def wait_rsp_ready(): + timeout = 60 + logger.info("wait for working RSP boards ") + sys.stdout.flush() + while timeout > 0: + answer = rspctl('--version') + # print answer + if answer.count('No Response') > 0: + time.sleep(5.0) + timeout -= 5 + if timeout < 60: + return 0 + continue + # check if image_nr > 0 for all boards + if answer.count('0.0') == 0: + logger.debug("All boards in working image") + return 1 + else: + logger.warning("Not all RSP boards in working image") + logger.debug(answer) + if not testmode: + time.sleep(5.0) + timeout -= 1 + return 0 + + +def check_active_boards(db, n_rsp_boards, n_tbb_boards, n_retries): + # check if RSPDriver is running + rsp_ready = False + tbb_ready = False + restarts = n_retries + + # wait for RSP boards ready, and reset 48V if needed, max 2x if no board errors after 48V reset + while (not rsp_ready or not tbb_ready) and restarts > 0: + if not check_active_rspdriver() or not check_active_tbbdriver(): + logger.warning('RSPDriver and/or TBBDriver not running') + swlevel(1) + swlevel(2) + #time.sleep(30.0) + + rsp_ready = wait_rsp_ready() + tbb_ready, tbbs_active = wait_tbb_ready(n_tbb_boards) + if not tbb_ready: + active_tbbs_changed = False + for tbb_nr, active in enumerate(tbbs_active): + if active != db.tbb[tbb_nr].board_active: + active_tbbs_changed = True + if not active_tbbs_changed: + tbb_ready = True + + if rsp_ready and tbb_ready : + break + + if not rsp_ready: + logger.warning('Not all RSP boards ready, reset 48V to recover') + if not rsp_ready: + logger.warning('Not all TBB boards ready, reset 48V to recover') + swlevel(1) + reset_48_volt() + restarts -= 1 + time.sleep(10.0) + level, board_errors = swlevel(2) + if len(board_errors) > 0: + db.board_errors = board_errors + + if not rsp_ready: + logger.warning('RSP not all boards ready') + if not tbb_ready: + logger.warning('TBB not all boards ready') + # put tbbs active information in the db + for tbb_nr, active in enumerate(tbbs_active): + if active in (False, None): + db.tbb[tbb_nr].board_active = 0 + else: + db.tbb[tbb_nr].board_active = 1 + return rsp_ready, tbb_ready + + +def mode_to_band(mode): + bands = {'10_90' : (1, 3), + '30_90' : (2, 4), + '110_190': (5,), + '170_210': (6,), + '210_250': (7,)} + for band, modes in bands.iteritems(): + if mode in modes: + return band + return '0' + + +# convert select-list to select-string +def select_str(sel_list): + last_sel = -2 + is_set = False + select = "" + for sel in sorted(sel_list): + if sel == last_sel + 1: + is_set = True + else: + if is_set: + is_set = False + select += ':%d' % last_sel + select += ",%d" % sel + last_sel = sel + if is_set: + select += ':%d' % last_sel + return select[1:] + + +# convert select-string to sel_list +def extract_select_str(select_string): + select_string = select_string.strip() + if not select_string: + return [] + select_list = list() + str_number = '' + int_number = None + first_set_number = None + is_set = False + #for ch in sel_str: + select_string_size = len(select_string) + last_i = select_string_size - 1 + for i in xrange(select_string_size): + ch = select_string[i] + if is_set and ch in '.': + continue + + if ch.isalnum(): + str_number += ch + if i < last_i: + continue + + if str_number: + int_number = int(str_number.strip()) + str_number = '' + + if int_number and (ch in ',' or i == last_i): + if is_set: + for nr in xrange(first_set_number, int_number+1, 1): + select_list.append(nr) + is_set = False + else: + select_list.append(int_number) + int_number = None + + if ch in ':.': + first_set_number = int_number + is_set = True + + return sorted(select_list) + + +def get_clock(): + answer = rspctl("--clock") + # print answer[-6:-3] + clock = float(answer[-7:-4]) + return clock + + +# function used for antenna testing +def swap_xy(state): + if state in (0, 1): + if state == 1: + logger.debug("XY-output swapped") + else: + logger.debug("XY-output normal") + rspctl('--swapxy=%d' % state) + + +def reset_rsp_settings(): + if rspctl('--clock').find('200MHz') < 0: + rspctl('--clock=200') + logger.debug("Changed Clock to 200MHz") + if not testmode: + time.sleep(2.0) + rspctl('--wg=0', wait=0.0) + rspctl('--rcuprsg=0', wait=0.0) + rspctl('--datastream=0', wait=0.0) + rspctl('--splitter=0', wait=0.0) + rspctl('--specinv=0', wait=0.0) + rspctl('--bitmode=16', wait=0.0) + rspctl('--rcumode=0', wait=0.0) + rspctl('--rcuenable=0', wait=0.0) + rspctl('--swapxy=0', wait=0.0) # 0=normal, 1=swapped + # rspctl ('--hbadelays=%s' %(('128,'*16)[:-1]), wait=8.0) + + +def turn_on_rcus(mode, rcus): + select = select_str(rcus) + logger.debug("turn RCU's on, mode %d" % mode) + logger.debug("enable rcus") + rspctl('--rcuenable=1 --select=%s' % select, wait=0.0) + logger.debug("setweights") + rspctl('--aweights=8000,0', wait=0.0) + + if mode == 5: + rspctl('--specinv=1', wait=0.0) + else: + rspctl('--specinv=0', wait=0.0) + + logger.debug("set rcu mode") + rsp_rcu_mode(mode, rcus) + return + + +def turn_off_rcus(): + logger.debug("RCU's off, mode 0") + rspctl('--rcumode=0', wait=0.0) + rspctl('--rcuenable=0', wait=0.0) + rspctl('--aweights=0,0', wait=1.0) + + +# set rcu mode, if mode > 4(hba) turn on hba's in steps to avoid power dips +def rsp_rcu_mode(mode, rcus): + if mode in range(1, 8, 1): # all modes + select = select_str(rcus) + rspctl('--rcumode=%d --select=%s' % (mode, select), wait=6.0) + return 0 + return -1 + + +# set hba_delays in steps to avoid power dips, and discharge if needed +def rsp_hba_delay(delay, rcus, discharge=True): + global active_delay_str + + if delay == active_delay_str: + logger.debug("requested delay already active, skip hbadelay command") + return 1 + + select = select_str(rcus) + if discharge: + # count number of elements off in last command + n_hba_off = 0 + for i in active_delay_str.split(','): + if int(i, 10) & 0x02: + n_hba_off += 1 + + # count number of elements on in new command, and make discharge string + n_hba_on = 0 + discharge_str = '' + if n_hba_off > 0: + for i in delay.split(','): + if int(i, 10) & 0x02: + discharge_str += "2," + else: + discharge_str += "0," + n_hba_on += 1 + + # discharge if needed + if n_hba_off > 0 and n_hba_on > 0: + logger.debug("set hbadelays to 0 for 1 second") + rspctl('--hbadelay=%s --select=%s' % (discharge_str[:-1], select), wait=6.0) + + logger.debug("send hbadelay command") + rspctl('--hbadelay=%s --select=%s' % (delay, select), wait=6.0) + + active_delay_str = delay + return 0 diff --git a/LCU/checkhardware/checkhardware_lib/reporting.py b/LCU/checkhardware/checkhardware_lib/reporting.py new file mode 100644 index 0000000000000000000000000000000000000000..428fdb7d99cb5e0ff60bfc67c8d51b34149dbeb8 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/reporting.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python +""" +Make report from measurement, using information in test_db +""" +import logging +from general import get_date_time_str, get_short_date_str, get_hostname, MyTestLogger + +logger = logging.getLogger('main.reporting') +logger.debug("starting reporting logger") + + +def make_report(db, logdir): + # print logdir + date = get_short_date_str(db.check_start_time) + log = MyTestLogger(logdir, db.StID) + + log.add_line("%s,NFO,---,VERSIONS,%s" % (date, db.script_versions)) + + log.add_line("%s,NFO,---,STATION,NAME=%s" % (date, db.StID)) + + log.add_line("%s,NFO,---,RUNTIME,START=%s,STOP=%s" % ( + date, get_date_time_str(db.check_start_time), get_date_time_str(db.check_stop_time))) + + info = "" + bad = "" + for ant in db.lbl.ant: + if ant.on_bad_list == 1: + bad += "%d " % ant.nr_pvss + if len(bad) > 0: + info += "LBL=%s," % (bad[:-1]) + + bad = "" + for ant in db.lbh.ant: + if ant.on_bad_list == 1: + bad += "%d " % ant.nr_pvss + if len(bad) > 0: + info += "LBH=%s," % (bad[:-1]) + + bad = "" + for tile in db.hba.tile: + if tile.on_bad_list == 1: + bad += "%d " % tile.nr + if len(bad) > 0: + info += "HBA=%s," % (bad[:-1]) + if len(info) > 0: + log.add_line("%s,NFO,---,BADLIST,%s" % (date, info[:-1])) + + if db.rsp_driver_down: + log.add_line("%s,NFO,---,DRIVER,RSPDRIVER=DOWN" % date) + + if db.tbb_driver_down: + log.add_line("%s,NFO,---,DRIVER,TBBDRIVER=DOWN" % date) + + if len(db.board_errors): + boardstr = '' + for board in db.board_errors: + boardstr += "RSP-%d=DOWN," % board + log.add_line("%s,NFO,---,BOARD,%s" % (date, boardstr[:-1])) + + log.add_line("%s,NFO,---,CHECKS,%s" % (date, ",".join(db.tests_done))) + + log.add_line("%s,NFO,---,STATISTICS,BAD_LBL=%d,BAD_LBH=%d,BAD_HBA=%d,BAD_HBA0=%d,BAD_HBA1=%d" % ( + date, db.lbl.nr_bad_antennas, db.lbh.nr_bad_antennas, db.hba.nr_bad_tiles, + db.hba.nr_bad_tiles_0, db.hba.nr_bad_tiles_1)) + + spu_report(db, date, log) + rsp_report(db, date, log) + tbb_report(db, date, log) + + for lba in (db.lbl, db.lbh): + lba_report(lba, date, log) + + hba_tile_report(db, date, log) + hba_element_report(db, date, log) + + return + + +def spu_report(db, date, log): + logger.debug("generate spu report") + for spu in db.spu: + spu.test() + if not spu.voltage_ok: + valstr = '' + if not spu.rcu_ok: + valstr += ",RCU-5.0V=%3.1f" % spu.rcu_5_0_volt + if not spu.lba_ok: + valstr += ",LBA-8.0V=%3.1f" % spu.lba_8_0_volt + if not spu.hba_ok: + valstr += ",HBA-48V=%3.1f" % spu.hba_48_volt + if not spu.spu_ok: + valstr += ",SPU-3.3V=%3.1f" % spu.spu_3_3_volt + if len(valstr): + log.add_line("%s,SPU,%03d,VOLTAGE%s" % (date, spu.nr, valstr)) + + if not spu.temp_ok: + log.add_line("%s,SPU,%03d,TEMPERATURE,PCB=%2.0f" % ( + date, spu.nr, spu.temp)) + + +def rsp_report(db, date, log): + logger.debug("generate rsp report") + for rsp in db.rsp: + rsp.test() + if not rsp.version_ok: + log.add_line("%s,RSP,%03d,VERSION,BP=%s,AP=%s" % ( + date, rsp.nr, rsp.bp_version, rsp.ap_version)) + if not rsp.voltage_ok: + log.add_line("%s,RSP,%03d,VOLTAGE,1.2V=%3.2f,2.5V=%3.2f,3.3V=%3.2f" % ( + date, rsp.nr, rsp.voltage1_2, rsp.voltage2_5, rsp.voltage3_3)) + if not rsp.temp_ok: + log.add_line("%s,RSP,%03d,TEMPERATURE,PCB=%2.0f,BP=%2.0f,AP0=%2.0f,AP1=%2.0f,AP2=%2.0f,AP3=%2.0f" % ( + date, rsp.nr, rsp.pcb_temp, rsp.bp_temp, rsp.ap0_temp, rsp.ap1_temp, rsp.ap2_temp, + rsp.ap3_temp)) + + +def tbb_report(db, date, log): + logger.debug("generate tbb report") + for tbb in db.tbb: + tbb.test() + if not tbb.version_ok: + log.add_line("%s,TBB,%03d,VERSION,TP=%s,MP=%s" % ( + date, tbb.nr, tbb.tp_version, tbb.mp_version)) + if not tbb.memory_ok: + log.add_line("%s,TBB,%03d,MEMORY" % (date, tbb.nr)) + + if not tbb.voltage_ok: + log.add_line("%s,TBB,%03d,VOLTAGE,1.2V=%3.2f,2.5V=%3.2f,3.3V=%3.2f" % ( + date, tbb.nr, tbb.voltage1_2, tbb.voltage2_5, tbb.voltage3_3)) + if not tbb.temp_ok: + log.add_line("%s,TBB,%03d,TEMPERATURE,PCB=%2.0f,TP=%2.0f,MP0=%2.0f,MP1=%2.0f,MP2=%2.0f,MP3=%2.0f" % ( + date, tbb.nr, tbb.pcb_temp, tbb.tp_temp, tbb.mp0_temp, tbb.mp1_temp, tbb.mp2_temp, + tbb.mp3_temp)) + + +def rcu_report(db, date, log): + logger.debug("generate rcu rapport") + for rcu in range(db.nr_rcu): + if db.rcu_state[rcu]: + log.add_line("%s,RCU,%03d,BROKEN" % (date, rcu)) + + +def lba_report(db, date, log): + logger.debug("generate %s report" % db.label.lower()) + if db.signal_check_done: + if db.rf_signal_to_low: + log.add_line("%s,%s,---,TOOLOW,MEDIANX=%3.1f,MEDIANY=%3.1f" % ( + date, db.label, db.rf_ref_signal_x, db.rf_ref_signal_y)) + else: + if db.error: + log.add_line("%s,%s,---,TESTSIGNAL,SUBBANDX=%d,SIGNALX=%3.1f,SUBBANDY=%d,SIGNALY=%3.1f" % ( + date, db.label, db.rf_test_subband_x, db.rf_ref_signal_x, + db.rf_test_subband_y, db.rf_ref_signal_y)) + + if db.noise_check_done or db.oscillation_check_done or db.spurious_check_done or \ + db.signal_check_done or db.short_check_done or db.flat_check_done or db.down_check_done: + for ant in db.ant: + if ant.down: + log.add_line("%s,%s,%03d,DOWN,X=%3.1f,Y=%3.1f,Xoff=%d,Yoff=%d" % ( + date, db.label, ant.nr_pvss, ant.x.down_pwr, ant.y.down_pwr, ant.x.down_offset, ant.y.down_offset)) + else: + if db.signal_check_done: + valstr = '' + if ant.x.too_low or ant.x.too_high: + valstr += ",X=%3.1f" % ant.x.test_signal + if ant.y.too_low or ant.y.too_high: + valstr += ",Y=%3.1f" % ant.y.test_signal + if len(valstr): + log.add_line("%s,%s,%03d,RF_FAIL%s" % (date, db.label, ant.nr_pvss, valstr)) + + if db.flat_check_done: + valstr = '' + if ant.x.flat: + valstr += ",Xmean=%3.1f" % ant.x.flat_val + if ant.y.flat: + valstr += ",Ymean=%3.1f" % ant.y.flat_val + if len(valstr): + log.add_line("%s,%s,%03d,FLAT%s" % (date, db.label, ant.nr_pvss, valstr)) + + if db.short_check_done: + valstr = '' + if ant.x.short: + valstr += ",Xmean=%3.1f" % ant.x.short_val + if ant.y.short: + valstr += ",Ymean=%3.1f" % ant.y.short_val + if len(valstr): + log.add_line("%s,%s,%03d,SHORT%s" % (date, db.label, ant.nr_pvss, valstr)) + + if db.oscillation_check_done: + valstr = '' + if ant.x.osc: + valstr += ',X=1' + if ant.y.osc: + valstr += ',Y=1' + if len(valstr): + log.add_line("%s,%s,%03d,OSCILLATION%s" % (date, db.label, ant.nr_pvss, valstr)) + + if db.spurious_check_done: + valstr = '' + if ant.x.spurious: + valstr += ',X=1' + if ant.y.spurious: + valstr += ',Y=1' + if len(valstr): + log.add_line("%s,%s,%03d,SPURIOUS%s" % (date, db.label, ant.nr_pvss, valstr)) + + if db.noise_check_done: + noise = False + valstr = '' + if not ant.x.flat and ant.x.low_noise: + proc = (100.0 / ant.x.low_seconds) * ant.x.low_bad_seconds + valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' % ( + proc, ant.x.low_val, ant.x.low_diff, ant.x.low_ref) + if not ant.y.flat and ant.y.low_noise: + proc = (100.0 / ant.y.low_seconds) * ant.y.low_bad_seconds + valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' % ( + proc, ant.y.low_val, ant.y.low_diff, ant.y.low_ref) + if len(valstr): + log.add_line("%s,%s,%03d,LOW_NOISE%s" % (date, db.label, ant.nr_pvss, valstr)) + noise = True + + valstr = '' + if ant.x.high_noise: + proc = (100.0 / ant.x.high_seconds) * ant.x.high_bad_seconds + valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' % ( + proc, ant.x.high_val, ant.x.high_diff, ant.x.high_ref) + if ant.y.high_noise: + proc = (100.0 / ant.y.high_seconds) * ant.y.high_bad_seconds + valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' % ( + proc, ant.y.high_val, ant.y.high_diff, ant.y.high_ref) + if len(valstr): + log.add_line("%s,%s,%03d,HIGH_NOISE%s" % (date, db.label, ant.nr_pvss, valstr)) + noise = True + + valstr = '' + if not noise and ant.x.jitter: + proc = (100.0 / ant.x.jitter_seconds) * ant.x.jitter_bad_seconds + valstr += ',Xproc=%5.3f,Xdiff=%5.3f,Xref=%3.1f' % ( + proc, ant.x.jitter_val, ant.x.jitter_ref) + if not noise and ant.y.jitter: + proc = (100.0 / ant.y.jitter_seconds) * ant.y.jitter_bad_seconds + valstr += ',Xproc=%5.3f,Ydiff=%5.3f,Yref=%3.1f' % ( + proc, ant.y.jitter_val, ant.y.jitter_ref) + if len(valstr): + log.add_line("%s,%s,%03d,JITTER%s" % (date, db.label, ant.nr_pvss, valstr)) + # lba = None + + +def hba_tile_report(db, date, log): + logger.debug("generate hba-tile report") + if db.hba.signal_check_done: + if db.hba.rf_signal_to_low: + log.add_line("%s,HBA,---,TOOLOW,MEDIANX=%3.1f,MEDIANY=%3.1f" % ( + date, db.hba.rf_ref_signal_x[0], db.hba.rf_ref_signal_y[0])) + else: + if db.hba.error: + log.add_line("%s,HBA,---,TESTSIGNAL,SUBBANDX=%d,SIGNALX=%3.1f,SUBBANDY=%d,SIGNALY=%3.1f" % ( + date, db.hba.rf_test_subband_x[0], db.hba.rf_ref_signal_x[0], + db.hba.rf_test_subband_y[0], db.hba.rf_ref_signal_y[0])) + + for tile in db.hba.tile: + if tile.x.error or tile.y.error: + # check for broken summators + if db.hba.modem_check_done: + valstr = '' + if tile.c_summator_error: + log.add_line("%s,HBA,%03d,C_SUMMATOR" % (date, tile.nr)) + else: + for elem in tile.element: + if elem.no_modem: + valstr += ",E%02d=??" % elem.nr + + elif elem.modem_error: + valstr += ",E%02d=error" % elem.nr + if len(valstr): + log.add_line("%s,HBA,%03d,MODEM%s" % (date, tile.nr, valstr)) + + if db.hba.noise_check_done: + valstr = '' + noise = False + + if tile.x.low_noise: + proc = (100.0 / tile.x.low_seconds) * tile.x.low_bad_seconds + valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' % ( + proc, tile.x.low_val, tile.x.low_diff, tile.x.low_ref) + if tile.y.low_noise: + proc = (100.0 / tile.y.low_seconds) * tile.y.low_bad_seconds + valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' % ( + proc, tile.y.low_val, tile.y.low_diff, tile.y.low_ref) + if len(valstr): + log.add_line("%s,HBA,%03d,LOW_NOISE%s" % (date, tile.nr, valstr)) + noise = True + + valstr = '' + if tile.x.high_noise: + proc = (100.0 / tile.x.high_seconds) * tile.x.high_bad_seconds + valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' % ( + proc, tile.x.high_val, tile.x.high_diff, tile.x.high_ref) + if tile.y.high_noise: + proc = (100.0 / tile.y.high_seconds) * tile.y.high_bad_seconds + valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' % ( + proc, tile.y.high_val, tile.y.high_diff, tile.y.high_ref) + if len(valstr): + log.add_line("%s,HBA,%03d,HIGH_NOISE%s" % (date, tile.nr, valstr)) + noise = True + + valstr = '' + if (not noise) and tile.x.jitter: + proc = (100.0 / tile.x.jitter_seconds) * tile.x.jitter_bad_seconds + valstr += ',Xproc=%5.3f,Xdiff=%5.3f,Xref=%3.1f' % (proc, tile.x.jitter_val, tile.x.jitter_ref) + if (not noise) and tile.y.jitter: + proc = (100.0 / tile.y.jitter_seconds) * tile.y.jitter_bad_seconds + valstr += ',Yproc=%5.3f,Ydiff=%5.3f,Yref=%3.1f' % (proc, tile.y.jitter_val, tile.y.jitter_ref) + if len(valstr): + log.add_line("%s,HBA,%03d,JITTER%s" % (date, tile.nr, valstr)) + + if db.hba.oscillation_check_done: + valstr = '' + if tile.x.osc: + valstr += ',X=1' + if tile.y.osc: + valstr += ',Y=1' + if len(valstr): + log.add_line("%s,HBA,%03d,OSCILLATION%s" % (date, tile.nr, valstr)) + + if tile.p_summator_error: + log.add_line("%s,HBA,%03d,P_SUMMATOR" % (date, tile.nr)) + + if db.hba.summatornoise_check_done: + valstr = '' + if tile.x.summator_noise: + valstr += ',X=1' + if tile.y.summator_noise: + valstr += ',Y=1' + if len(valstr): + log.add_line("%s,HBA,%03d,SUMMATOR_NOISE%s" % (date, tile.nr, valstr)) + + if db.hba.spurious_check_done: + valstr = '' + if tile.x.spurious: + valstr += ',X=1' + if tile.y.spurious: + valstr += ',Y=1' + if len(valstr): + log.add_line("%s,HBA,%03d,SPURIOUS%s" % (date, tile.nr, valstr)) + + if db.hba.signal_check_done: + valstr = '' + if tile.x.too_low or tile.x.too_high: + valstr += ",X=%3.1f %d %3.1f %3.1f %d %3.1f" % ( + tile.x.test_signal[0], db.hba.rf_test_subband_x[0], db.hba.rf_ref_signal_x[0], + tile.x.test_signal[1], db.hba.rf_test_subband_x[1], db.hba.rf_ref_signal_x[1]) + if tile.y.too_low or tile.y.too_high: + valstr += ",Y=%3.1f %d %3.1f %3.1f %d %3.1f" % ( + tile.y.test_signal[0], db.hba.rf_test_subband_y[0], db.hba.rf_ref_signal_y[0], + tile.y.test_signal[1], db.hba.rf_test_subband_y[1], db.hba.rf_ref_signal_y[1]) + if len(valstr): + log.add_line("%s,HBA,%03d,RF_FAIL%s" % (date, tile.nr, valstr)) + + +def hba_element_report(db, date, log): + logger.debug("generate hba-element report") + if not db.hba.element_check_done: + return + + for tile in db.hba.tile: + valstr = '' + for elem in tile.element: + # if tile.x.rcu_off or tile.y.rcu_off: + # continue + if db.hba.modem_check_done and (elem.no_modem or elem.modem_error): + if not tile.c_summator_error: + if elem.no_modem: + valstr += ",M%d=??" % elem.nr + + elif elem.modem_error: + valstr += ",M%d=error" % elem.nr + else: + if elem.x.osc or elem.y.osc: + if elem.x.osc: + valstr += ",OX%d=1" % elem.nr + if elem.y.osc: + valstr += ",OY%d=1" % elem.nr + + elif elem.x.spurious or elem.y.spurious: + if elem.x.spurious: + valstr += ",SPX%d=1" % elem.nr + if elem.y.spurious: + valstr += ",SPY%d=1" % elem.nr + + elif elem.x.low_noise or elem.x.high_noise or elem.y.low_noise or \ + elem.y.high_noise or elem.x.jitter or elem.y.jitter: + if elem.x.low_noise: + valstr += ",LNX%d=%3.1f %5.3f" % (elem.nr, elem.x.low_val, elem.x.low_diff) + + if elem.x.high_noise: + valstr += ",HNX%d=%3.1f %5.3f" % (elem.nr, elem.x.high_val, elem.x.high_diff) + + if (not elem.x.low_noise) and (not elem.x.high_noise) and (elem.x.jitter > 0.0): + valstr += ",JX%d=%5.3f" % (elem.nr, elem.x.jitter) + + if elem.y.low_noise: + valstr += ",LNY%d=%3.1f %5.3f" % (elem.nr, elem.y.low_val, elem.y.low_diff) + + if elem.y.high_noise: + valstr += ",HNY%d=%3.1f %5.3f" % (elem.nr, elem.y.high_val, elem.y.high_diff) + + if (not elem.y.low_noise) and (not elem.y.high_noise) and (elem.y.jitter > 0.0): + valstr += ",JY%d=%5.3f" % (elem.nr, elem.y.jitter) + else: + #logger.debug("check signal elem %d" % elem.nr) + if elem.x.rf_ref_signal[0] == 0 and elem.x.rf_ref_signal[1] == 0: + #logger.debug("x ref signal == 0") + log.add_line("%s,HBA,%03d,NOSIGNAL,E%02dX" % (date, + tile.nr, + elem.nr)) + else: + if elem.x.error == 1: + #logger.debug("x-error") + valstr += ",X%d=%3.1f %d %3.1f %3.1f %d %3.1f" % (elem.nr, + elem.x.test_signal[0], + elem.x.rf_test_subband[0], + elem.x.rf_ref_signal[0], + elem.x.test_signal[1], + elem.x.rf_test_subband[1], + elem.x.rf_ref_signal[1]) + + if elem.y.rf_ref_signal[0] == 0 and elem.y.rf_ref_signal[1] == 0: + #logger.debug("y ref signal == 0") + log.add_line("%s,HBA,%03d,NOSIGNAL,E%02dY" % (date, + tile.nr, + elem.nr)) + else: + if elem.y.error == 1: + #logger.debug("y-error") + valstr += ",Y%d=%3.1f %d %3.1f %3.1f %d %3.1f" % (elem.nr, + elem.y.test_signal[0], + elem.y.rf_test_subband[0], + elem.y.rf_ref_signal[0], + elem.y.test_signal[1], + elem.y.rf_test_subband[1], + elem.y.rf_ref_signal[1]) + + if len(valstr): + logger.debug("tile %d valstr=%s" % (tile.nr, valstr)) + log.add_line("%s,HBA,%03d,E_FAIL%s" % (date, + tile.nr, + valstr)) diff --git a/LCU/checkhardware/checkhardware_lib/rsp.py b/LCU/checkhardware/checkhardware_lib/rsp.py new file mode 100644 index 0000000000000000000000000000000000000000..297830d9082a95d897187d78ce37f443030d1d70 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/rsp.py @@ -0,0 +1,150 @@ + +import numpy as np +from lofar import * + +logger = logging.getLogger('main.rsp') +logger.debug("starting rsp logger") + +# class for checking RSP boards using rspctl +class RSP(object): + def __init__(self, db): + self.db = db + self.nr = self.db.nr_rsp + + # check software versions of driver, tbbctl and TP/MP firmware + def check_versions(self, parset): + logger.info("=== RSP Version check ===") + #print parset.get_set() + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + answer = rspctl('--version') + + # check if Driver is available + if answer.find('No Response') > 0: + logger.warning("No RSPDriver") + images_ok = False + else: + infolines = answer.splitlines() + info = infolines + + images_ok = True + # check if image_nr > 0 for all boards + if str(info).count('0.0') != 0: + logger.warning("Not all boards in working image") + images_ok = False + + for rsp in self.db.rsp: + board_info = info[rsp.nr].split(',') + + if board_info[1].split()[3] != parset.as_string('version.bp'): + logger.warning("Board %d Not right BP version" % rsp.nr) + rsp.bp_version = board_info[1].split()[3] + images_ok = False + + if board_info[2].split()[3] != parset.as_string('version.ap'): + logger.warning("Board %d Not right AP version" % rsp.nr) + rsp.ap_version = board_info[2].split()[3] + images_ok = False + + if not check_active_rspdriver(): + logger.warning("RSPDriver down while testing, skip result") + return False + + logger.info("=== Done RSP Version check ===") + self.db.add_test_done('RV') + return images_ok + + def check_board(self, parset): + ok = True + logger.info("=== RSP Board check ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return False + answer = rspctl('--status') + + bp_temp = np.zeros((self.db.nr_rsp,), float) + bp_temp[:] = -1 + + ap_temp = np.zeros((self.db.nr_rsp, 4), float) + ap_temp[:,:] = -1 + + p2 = 0 + for rsp in self.db.rsp: + p1 = answer.find("RSP[%2d]" % rsp.nr, p2) + p2 = answer.find("\n", p1) + d = [float(i.split(":")[1].strip()) for i in answer[p1 + 7:p2].split(',')] + if len(d) == 3: + logger.debug("RSP board %2d, voltages: 1.2V=%4.2f, 2.5V=%4.2f, 3.3V=%4.2f" % ( + rsp.nr, d[0], d[1], d[2])) + rsp.voltage1_2 = d[0] + rsp.voltage2_5 = d[1] + rsp.voltage3_3 = d[2] + + for rsp in self.db.rsp: + p1 = answer.find("RSP[%2d]" % rsp.nr, p2) + p2 = answer.find("\n", p1) + d = [float(i.split(":")[1].strip()) for i in answer[p1 + 7:p2].split(',')] + if len(d) == 6: + logger.debug("RSP board %2d, temperatures: pcb=%3.0f, bp=%3.0f, ap0=%3.0f, ap1=%3.0f, ap2=%3.0f, ap3=%3.0f" % ( + rsp.nr, d[0], d[1], d[2], d[3], d[4], d[5])) + rsp.pcb_temp = d[0] + rsp.bp_temp = d[1] + rsp.ap0_temp = d[2] + rsp.ap1_temp = d[3] + rsp.ap2_temp = d[4] + rsp.ap3_temp = d[5] + bp_temp[rsp.nr] = d[1] + ap_temp[rsp.nr, 0] = d[2] + ap_temp[rsp.nr, 1] = d[3] + ap_temp[rsp.nr, 2] = d[4] + ap_temp[rsp.nr, 3] = d[5] + + bp_temp = np.ma.masked_less(bp_temp, 0) + bp_check_temp = np.ma.median(bp_temp[:]) + parset.as_float('temperature.bp.max_delta') + logger.debug("bp avarage temp= %3.1f, check temperature= %3.1f" % ( + np.ma.mean(bp_temp[:]), bp_check_temp)) + + ap_temp = np.ma.masked_less(ap_temp, 0) + ap_check_temp = np.ma.median(ap_temp[:,:]) + parset.as_float('temperature.ap.max_delta') + logger.debug("ap avarage temp= %3.1f, check temperature= %3.1f" % ( + np.ma.mean(ap_temp[:,:]), ap_check_temp)) + + for rsp in self.db.rsp: + if not (parset.as_float('voltage.1_2.min') <= rsp.voltage1_2 <= parset.as_float('voltage.1_2.max')): + rsp.voltage_ok = 0 + logger.info("RSP board %2d bad voltage 1.2V=%4.2fV" % (rsp.nr, rsp.voltage1_2)) + if not (parset.as_float('voltage.2_5.min') <= rsp.voltage2_5 <= parset.as_float('voltage.2_5.max')): + rsp.voltage_ok = 0 + logger.info("RSP board %2d bad voltage 2.5V=%4.2fV" % (rsp.nr, rsp.voltage2_5)) + if not (parset.as_float('voltage.3_3.min') <= rsp.voltage3_3 <= parset.as_float('voltage.3_3.max')): + rsp.voltage_ok = 0 + logger.info("RSP board %2d bad voltage 3.3V=%4.2fV" % (rsp.nr, rsp.voltage3_3)) + + if rsp.pcb_temp > parset.as_float('temperature.max'): + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature pcb_temp=%3.0f" % (rsp.nr, rsp.pcb_temp)) + + if rsp.bp_temp > parset.as_float('temperature.bp.max') or rsp.bp_temp > bp_check_temp: + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature bp_temp=%3.0f" % (rsp.nr, rsp.bp_temp)) + + if rsp.ap0_temp > parset.as_float('temperature.ap.max') or rsp.ap0_temp > ap_check_temp: + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature ap0_temp=%3.0f" % (rsp.nr, rsp.ap0_temp)) + + if rsp.ap1_temp > parset.as_float('temperature.ap.max') or rsp.ap1_temp > ap_check_temp: + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature ap1_temp=%3.0f" % (rsp.nr, rsp.ap1_temp)) + + if rsp.ap2_temp > parset.as_float('temperature.ap.max') or rsp.ap2_temp > ap_check_temp: + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature ap2_temp=%3.0f" % (rsp.nr, rsp.ap2_temp)) + + if rsp.ap3_temp > parset.as_float('temperature.ap.max') or rsp.ap3_temp > ap_check_temp: + rsp.temp_ok = 0 + logger.info("RSP board %2d, high temperature ap3_temp=%3.0f" % (rsp.nr, rsp.ap3_temp)) + logger.info("=== Done RSP Board check ===") + self.db.add_test_done('RBC') + return ok + # end of RSP class diff --git a/LCU/checkhardware/checkhardware_lib/settings.py b/LCU/checkhardware/checkhardware_lib/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..7d0b3b5548bcb41540f9cb3d6ad2117aa349a964 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/settings.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python + +""" +Test settings for all test, settings are read from checkhardware.conf +""" +import logging +from lofar import extract_select_str + +logger = logging.getLogger('main.settings') +logger.debug("starting settings logger") + + +class TestSettings(object): + def __init__(self, filename): + self.parset = ParameterSet() + self.parset.import_file(filename) + + def __call__(self): + return self.parset + + def group(self, dev): + pre_key = dev + ps = ParameterSet(self.parset.make_subset(pre_key)) + return ps + + def rcumode(self, mode): + pre_key = 'rcumode' + if mode in (1, 3): + pre_key += '.1-3' + elif mode in (2, 4): + pre_key += '.2-4' + elif mode in (5, 6, 7): + pre_key += '.%d' % mode + ps = ParameterSet(self.parset.make_subset(pre_key)) + return ps + + +class ParameterSet(object): + def __init__(self, data=None): + self.parset = {} + if data: + if type(data) == dict: + self.parset = data + elif type(data) == str: + self.import_string(data) + + def clear(self): + self.parset = {} + + def get_set(self): + return self.parset + + def import_file(self, filename): + fd = open(filename, 'r') + data = fd.readlines() + fd.close() + self.import_string(data) + #for k, v in self.parset.iteritems(): + # logger.debug("parset: %s=%s" % (str(k), str(v))) + + def import_string(self, data): + pre_key = '' + for line in data: + if line.strip() == '' or line.startswith('#'): + continue + if line.startswith('['): + pre_key = line[line.find('[') + 1: line.find(']')] + continue + + ps = self.parset + kv_pair = line.strip().split('=') + key = kv_pair[0] + try: + value = kv_pair[1] + except IndexError: + logger.debug("%s has no value" % key) + value = '' + except: + raise + full_key = pre_key + '.' + key + key_list = full_key.strip().split('.') + # print key_list, key_list[-1] + + last_key = key_list[-1] # .replace('_', '.') + for k in key_list[:-1]: + # k = k.replace('_', '.') + if k in ps: + ps = ps[k] + else: + ps[k] = {} + ps = ps[k] + ps[last_key] = value.strip() + + def make_subset(self, subset): + keys = subset.strip().split('.') + ps = self.parset + for key in keys: + ps = ps[key] + return ps + + def replace(self, key, value): + self.parset[key] = value + + def as_int(self, key, default=None): + value = self.get(key, default) + if value is None: + return 0 + try: + return int(value) + except ValueError: + logger.error("wrong conversion to integer") + return 0 + except: + raise + + def as_int_list(self, key, default=None): + value_str = self.get(key, default) + if value_str is None: + return [] + return extract_select_str(value_str) + + def as_float(self, key, default=None): + value = self.get(key, default) + if value is None: + return 0.0 + try: + return float(value) + except ValueError: + logger.error("wrong conversion to float") + return 0.0 + except: + raise + + def as_string(self, key, default=None): + value = self.get(key, default) + if value is None: + return '' + try: + return str(value).strip() + except ValueError: + logger.error("wrong conversion to string") + return '' + except: + raise + + def get(self, key, default_val=None): + ps = self.parset + keys = key.split('.') + try: + for k in keys: + ps = ps[k] + except KeyError: + ps = default_val + logger.debug("key %s not found return default value" % key) + except: + raise + value = ps + return value diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/__init__.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5fa857b8386cce82664a577f68fa2bfc2db4ff91 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/__init__.py @@ -0,0 +1,10 @@ + +from flat import check_for_flat +from short import check_for_short +from down import check_for_down +from noise import check_for_noise +from spurious import check_for_spurious +from oscillation import check_for_oscillation +from rf_power import check_rf_power +from summator_noise import check_for_summator_noise +from cable_reflection import check_for_cable_reflection diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/cable_reflection.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/cable_reflection.py new file mode 100644 index 0000000000000000000000000000000000000000..45a3c6dc534dafb25b365be8d007f2e7e1c39018 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/cable_reflection.py @@ -0,0 +1,62 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.cab..') +logger.debug("init logger") + +def check_for_cable_reflection(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_peak_pwr = parset.as_float('cable-reflection.min-peak-pwr') + passband = parset.as_int_list('cable-reflection.passband') + data.set_passband(band, passband) + + cr_info = list() # cable reflection + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + + secs = _data.shape[1] + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d data_nr=%d" %(rcu, data_nr)) + sum_cr_peaks = 0 + max_peaks = 0 + for sec in range(secs): + peaks_ref = SearchPeak(_data[:, sec, :].mean(axis=0)) + if peaks_ref.valid_data: + peaks_ref.search(delta=min_peak_pwr) + + peaks = SearchPeak(_data[data_nr, sec, :]) + if peaks.valid_data: + peaks.search(delta=min_peak_pwr, skip_list=peaks_ref.max_peaks) + n_peaks = peaks.n_max_peaks() + if n_peaks < 3: + continue + cr_peaks = 0 + last_sb, min_sb, max_sb = peaks.max_peaks[0] + last_sb_val = peaks.get_peak_value(last_sb) + for sb, min_sb, max_sb in peaks.max_peaks[1:]: + sb_val = peaks.get_peak_value(sb) + + sb_diff = sb - last_sb + sb_val_diff = sb_val - last_sb_val + if sb_diff in (6, 7): + if abs(sb_val_diff) < 2.0: + cr_peaks += 1 + elif cr_peaks < 6 and abs(sb_val_diff) > 3.0: + cr_peaks = 0 + last_sb = sb + last_sb_val = sb_val + + sum_cr_peaks += cr_peaks + max_peaks = max(max_peaks, n_peaks) + + if sum_cr_peaks > (secs * 3.0): + cr_peaks = sum_cr_peaks / secs + cr_info.append((rcu, cr_peaks, max_peaks)) + + data.reset_masked_sb(band) + return cr_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/down.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/down.py new file mode 100644 index 0000000000000000000000000000000000000000..2137c774ad08cc7d861ccd15e3e521dd275c9974 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/down.py @@ -0,0 +1,245 @@ +from peakslib import * + +logger = logging.getLogger('main.chk.dow..') +logger.debug("init logger") + +def check_for_down(data, band, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param parset: parameterset with check settings + :return: list with found failures + """ + subbands =parset.as_int_list('down.passband') + if subbands is None: + logger.warning("no passband found, use default 250:350") + subbands = range(250,351,1) + + down_info = list() + shifted_info = list() + start_sb = min(subbands) + stop_sb = max(subbands) + + normal_center_sb = 294 + normal_bw_3db = 45 + + x_data = data.median_spectras(freq_band=band, polarity='x', masked=True) + y_data = data.median_spectras(freq_band=band, polarity='y', masked=True) + median_spectra_x = ma.median(x_data, axis=0) + median_spectra_y = ma.median(y_data, axis=0) + + + x_data_delta = x_data - median_spectra_x + x_data_delta = x_data_delta + x_data_delta.min() + y_data_delta = y_data - median_spectra_y + y_data_delta = y_data_delta + y_data_delta.min() + + min_peak_width = 10 + + # get some median information + peaks = SearchPeak(median_spectra_x) + if not peaks.valid_data: + return down_info, shifted_info + + peaks.search(delta=3, min_width=min_peak_width) + #logger.debug("found X peaks with bandwidth > %d = %s" % (min_peak_width, str([i[0] for i in peaks.max_peaks]))) + + # search for nearest peak to center of subbands + x_median_max_peak_val = 0.0 + x_median_max_peak_sb = 0 + x_median_min_sb = 0 + x_median_max_sb = 0 + min_gap = 500 + for max_pos, min_sb, max_sb in peaks.max_peaks: + if abs(max_pos - normal_center_sb) < min_gap: + min_gap = abs(max_pos - normal_center_sb) + x_median_max_peak_val = peaks.get_peak_value(max_pos) + x_median_max_peak_sb = max_pos + x_median_min_sb = min_sb + x_median_max_sb = max_sb + + x_median_bandwidth = x_median_max_sb - x_median_min_sb + x_median_subbands_pwr = float(ma.median(x_data[:, start_sb: stop_sb])) + logger.debug("median X peak all rcu's in band %d .. %d : subband=%d, bw(3dB)=%d, median-value-band=%3.1fdB" % ( + start_sb, stop_sb, x_median_max_peak_sb, x_median_bandwidth, x_median_subbands_pwr)) + + down_info.append(('Xref', x_median_max_peak_sb, x_median_max_peak_val, x_median_bandwidth, x_median_subbands_pwr)) + + + peaks = SearchPeak(median_spectra_y) + if not peaks.valid_data: + return down_info, shifted_info + + peaks.search(delta=3, min_width=min_peak_width) + #logger.debug("found Y peaks with bandwidth > %d = %s" % (min_peak_width, str([i[0] for i in peaks.max_peaks]))) + + # search for nearest peak to center of subbands + y_median_max_peak_val = 0.0 + y_median_max_peak_sb = 0 + y_median_min_sb = 0 + y_median_max_sb = 0 + min_gap = 500 + for max_pos, min_sb, max_sb in peaks.max_peaks: + if abs(max_pos - normal_center_sb) < min_gap: + min_gap = abs(max_pos - normal_center_sb) + y_median_max_peak_val = peaks.get_peak_value(max_pos) + y_median_max_peak_sb = max_pos + y_median_min_sb = min_sb + y_median_max_sb = max_sb + + y_median_bandwidth = y_median_max_sb - y_median_min_sb + y_median_subbands_pwr = float(ma.median(x_data[:, start_sb: stop_sb])) + logger.debug("median Y peak all rcu's in band %d .. %d : subband=%d, bw(3dB)=%d, median-value-band=%3.1fdB" % ( + start_sb, stop_sb, y_median_max_peak_sb, y_median_bandwidth, y_median_subbands_pwr)) + + down_info.append(('Yref', y_median_max_peak_sb, y_median_max_peak_val, y_median_bandwidth, y_median_subbands_pwr)) + + + mean_pwr_array = zeros((data.max_rcus()), 'f') + peaks_pwr_array = zeros((data.max_rcus()), 'f') + peaks_sb_array = zeros((data.max_rcus()), 'i') + peaks_bw_array = zeros((data.max_rcus()), 'i') + + # for all rcus fill above arrays with data + for data_nr, rcu in enumerate(data.rcus(band, 'x')): + mean_pwr_array[rcu] = float(ma.median(x_data[data_nr, start_sb: stop_sb])) + peaks = SearchPeak(x_data_delta[data_nr, :]) + if peaks.valid_data: + peaks.search(delta=3, min_width=min_peak_width) + #logger.debug("RCU %d found X peaks with bandwidth > %d = %s" % (rcu, min_peak_width, str([i[0] for i in peaks.max_peaks]))) + + # search for nearest peak to center of subbands + max_peak_val = 0.0 + max_peak_sb = 0 + min_sb = 0 + max_sb = 0 + min_gap = 500 + for _max_pos, _min_sb, _max_sb in peaks.max_peaks: + if _max_pos not in subbands: + continue + if abs(_max_pos - normal_center_sb) < min_gap: + min_gap = abs(_max_pos - normal_center_sb) + max_peak_val = x_data[data_nr, _max_pos] + max_peak_sb = _max_pos + min_sb = _min_sb + max_sb = _max_sb + + if max_peak_sb > 0: + logger.debug("RCU %d X: max. peak(s) found on subband(s) %s" % (rcu, str([i[0] for i in peaks.max_peaks]))) + peaks = SearchPeak(x_data[data_nr, :]) + peak_bandwidth, _min_sb, _max_sb = peaks.get_peak_width(max_peak_sb, 3) + # peakwidth, min_sb, max_sb = peaks.getPeakWidth(maxpeak_sb, delta) + peaks_bw_array[rcu] = peak_bandwidth + peaks_pwr_array[rcu] = max_peak_val + peaks_sb_array[rcu] = max_peak_sb + else: + peaks = SearchPeak(x_data[data_nr, :]) + peak_bandwidth, _min_sb, _max_sb = peaks.get_peak_width(normal_center_sb, 3) + peaks_bw_array[rcu] = peak_bandwidth + peaks_sb_array[rcu] = normal_center_sb + peaks_pwr_array[rcu] = x_data[data_nr, _max_pos] + + for data_nr, rcu in enumerate(data.rcus(band, 'y')): + mean_pwr_array[rcu] = float(ma.median(y_data[data_nr, start_sb: stop_sb])) + peaks = SearchPeak(y_data_delta[data_nr, :]) + if peaks.valid_data: + peaks.search(delta=3, min_width=min_peak_width) + #logger.debug("RCU %d found Y peaks with bandwidth > %d = %s" % (rcu, min_peak_width, str([i[0] for i in peaks.max_peaks]))) + + # search for nearest peak to center of subbands + max_peak_val = 0.0 + max_peak_sb = 0 + min_sb = 0 + max_sb = 0 + min_gap = 200 + for _max_pos, _min_sb, _max_sb in peaks.max_peaks: + if _max_pos not in subbands: + continue + if abs(_max_pos - normal_center_sb) < min_gap: + min_gap = abs(_max_pos - normal_center_sb) + max_peak_val = y_data[data_nr, _max_pos] + max_peak_sb = _max_pos + min_sb = _min_sb + max_sb = _max_sb + + + + if max_peak_sb > 0: + logger.debug("RCU %d Y: max. peak(s) found on subband(s) %s" % (rcu, str([i[0] for i in peaks.max_peaks]))) + peaks = SearchPeak(y_data[data_nr, :]) + peak_bandwidth, _min_sb, _max_sb = peaks.get_peak_width(max_peak_sb, 3) + # peakwidth, min_sb, max_sb = peaks.getPeakWidth(maxpeak_sb, delta) + peaks_bw_array[rcu] = peak_bandwidth + peaks_pwr_array[rcu] = max_peak_val + peaks_sb_array[rcu] = max_peak_sb + else: + peaks = SearchPeak(y_data[data_nr, :]) + peak_bandwidth, _min_sb, _max_sb = peaks.get_peak_width(normal_center_sb, 3) + peaks_bw_array[rcu] = peak_bandwidth + peaks_sb_array[rcu] = normal_center_sb + peaks_pwr_array[rcu] = y_data[data_nr, _max_pos] + + # check for down or shifted tops of the noise-floor + x_rcus = data.rcus(band, 'x') + for x_rcu in x_rcus: + if data.mode(x_rcu) in (1, 2): + y_rcu = x_rcu - 1 + else: + y_rcu = x_rcu + 1 + x_value_trigger = False + x_down_trigger = False + y_value_trigger = False + y_down_trigger = False + thunderstorm = False + logger.debug("rcu=%d: X-top, sb=%d, pwr=%3.1fdB, bw=%d, mean-value-band=%3.1f" % ( + x_rcu, peaks_sb_array[x_rcu], peaks_pwr_array[x_rcu], peaks_bw_array[x_rcu], mean_pwr_array[x_rcu])) + logger.debug("rcu=%d: Y-top, sb=%d, pwr=%3.1fdB, bw=%d, mean-value-band=%3.1f" % ( + y_rcu, peaks_sb_array[y_rcu], peaks_pwr_array[y_rcu], peaks_bw_array[y_rcu], mean_pwr_array[y_rcu])) + + # if both polarity's not shifted and 1 pol is higher then average and the other is lower then average + if abs(peaks_sb_array[x_rcu] - normal_center_sb) < 3 and abs(peaks_sb_array[y_rcu] - normal_center_sb) < 3: + if (mean_pwr_array[x_rcu] < (x_median_subbands_pwr - 3.0) and mean_pwr_array[y_rcu] > (y_median_subbands_pwr + 3.0)): + thunderstorm = True + if (mean_pwr_array[y_rcu] < (y_median_subbands_pwr - 3.0) and mean_pwr_array[x_rcu] > (x_median_subbands_pwr + 3.0)): + thunderstorm = True + + if thunderstorm: + logger.debug("skip down test, probably thunderstorm active") + else: + if 66.0 < mean_pwr_array[x_rcu] < (x_median_subbands_pwr - 2.0): + logger.debug("rcu=%d: mean signal in test band for X lower than normal" % x_rcu) + x_value_trigger = True + if 66.0 < mean_pwr_array[y_rcu] < (y_median_subbands_pwr - 2.0): + logger.debug("rcu=%d: mean signal in test band for Y lower than normal" % y_rcu) + y_value_trigger = True + + if abs(peaks_sb_array[x_rcu] - normal_center_sb) > 10 or abs(normal_bw_3db - peaks_bw_array[x_rcu]) > 10: + if peaks_bw_array[x_rcu] > 3: + logger.debug("rcu=%d: X broken or antenna down" % x_rcu) + x_down_trigger = True + if abs(peaks_sb_array[y_rcu] - normal_center_sb) > 10 or abs(normal_bw_3db - peaks_bw_array[y_rcu]) > 10: + if peaks_bw_array[y_rcu] > 3: + logger.debug("rcu=%d: Y broken or antenna down" % y_rcu) + y_down_trigger = True + + if (x_value_trigger and x_down_trigger) and (y_value_trigger and y_down_trigger): + down_info.append((x_rcu, peaks_sb_array[x_rcu], peaks_pwr_array[x_rcu], peaks_bw_array[x_rcu], mean_pwr_array[x_rcu])) + down_info.append((y_rcu, peaks_sb_array[y_rcu], peaks_pwr_array[y_rcu], peaks_bw_array[y_rcu], mean_pwr_array[y_rcu])) + #logger.debug("down_info=%s" % str(down_info)) + else: + if (peaks_bw_array[x_rcu] > 20) and (abs(peaks_sb_array[x_rcu] - normal_center_sb) > 10): + logger.debug("rcu=%d: X-top shifted normal=%d, now=%d" % ( + x_rcu, normal_center_sb, peaks_sb_array[x_rcu])) + shifted_info.append((x_rcu, peaks_sb_array[x_rcu], normal_center_sb)) + if (peaks_bw_array[y_rcu] > 20) and (abs(peaks_sb_array[y_rcu] - normal_center_sb) > 10): + logger.debug("rcu=%d: Y-top shifted normal=%d, now=%d" % ( + y_rcu, normal_center_sb, peaks_sb_array[y_rcu])) + shifted_info.append((y_rcu, peaks_sb_array[y_rcu], normal_center_sb)) + + + # if more than half the antennes are down or shifted, skip test + if len(down_info) > len(data.rcus(band, 'xy')) / 2: + down_info = list() + if len(shifted_info) > len(data.rcus(band, 'xy')) / 2: + shifted_info = list() + return down_info, shifted_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/down_old.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/down_old.py new file mode 100644 index 0000000000000000000000000000000000000000..d316b365e7d543122d23b7c2cd47afb5b9b9ff3d --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/down_old.py @@ -0,0 +1,162 @@ +from peakslib import * + +logger = logging.getLogger('main.chk.dow..') +logger.debug("init logger") + +def check_for_down(data, band, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param parset: parameterset with check settings + :return: list with found failures + """ + subbands =parset.as_int_list('down.passband') + if subbands is None: + logger.warning("no passband found, use default 250:350") + subbands = range(250,351,1) + + # _data = (rcus x subbands) + _data = data.median_spectras(freq_band=band, polarity='xy', masked=True) + down_info = list() + shifted_info = list() + start_sb = min(subbands) + stop_sb = max(subbands) + center_sb = (stop_sb + start_sb) / 2 + + peaks = SearchPeak(ma.median(_data, axis=0)) + if not peaks.valid_data: + return down_info, shifted_info + + min_peak_width = 3 + peaks.search(delta=3, min_width=min_peak_width) + logger.debug("found peaks with bandwidth > %d = %s" % (min_peak_width, str([i[0] for i in peaks.max_peaks]))) + + # search for nearest peak to center of subbands + median_max_peak_val = 0.0 + median_max_peak_sb = 0 + median_min_sb = 0 + median_max_sb = 0 + min_gap = 100 + for max_pos, min_sb, max_sb in peaks.max_peaks: + if abs(max_pos - center_sb) < min_gap: + min_gap = abs(max_pos - center_sb) + median_max_peak_val = peaks.get_peak_value(max_pos) + median_max_peak_sb = max_pos + median_min_sb = min_sb + median_max_sb = max_sb + + median_bandwidth = median_max_sb - median_min_sb + + #(median_max_val, median_max_sb, min_sb, max_sb) = peaks.get_max_peak(sb_list=subbands) + # peakwidth, min_sb, max_sb = peaks.getPeakWidth(median_max_sb, delta) + # median_max_sb += start_sb + + median_subbands_pwr = float(ma.median(_data[:, start_sb: stop_sb])) + logger.debug("reference peak in band %d .. %d : subband=%d, bw(3dB)=%d, median-value-band=%3.1fdB" % ( + start_sb, stop_sb, median_max_peak_sb, median_bandwidth, median_subbands_pwr)) + + down_info.append((-1, median_max_peak_sb, median_max_peak_val, median_bandwidth, median_subbands_pwr)) + + + mean_pwr_array = zeros((data.max_rcus()), 'f') + peaks_pwr_array = zeros((data.max_rcus()), 'f') + peaks_sb_array = zeros((data.max_rcus()), 'i') + peaks_bw_array = zeros((data.max_rcus()), 'i') + + for data_nr, rcu in enumerate(data.rcus(band, 'xy')): + mean_pwr_array[rcu] = float(ma.mean(_data[data_nr, start_sb: stop_sb])) + peaks = SearchPeak(_data[data_nr, :]) + if peaks.valid_data: + peaks.search(delta=3, min_width=3) + + # search for nearest peak to center of subbands + max_peak_val = 0.0 + max_peak_sb = 0 + min_sb = 0 + max_sb = 0 + min_gap = 100 + for _max_pos, _min_sb, _max_sb in peaks.max_peaks: + if abs(_max_pos - center_sb) < min_gap: + min_gap = abs(_max_pos - center_sb) + max_peak_val = peaks.get_peak_value(max_pos) + max_peak_sb = _max_pos + min_sb = _min_sb + max_sb = _max_sb + + peak_bandwidth = max_sb - min_sb + + #(maxpeak_val, maxpeak_sb, min_sb, max_sb) = peaks.get_max_peak(sb_list=subbads) + + if max_peak_sb > 0: + # peakwidth, min_sb, max_sb = peaks.getPeakWidth(maxpeak_sb, delta) + peaks_bw_array[rcu] = peak_bandwidth + peaks_pwr_array[rcu] = max_peak_val + peaks_sb_array[rcu] = max_peak_sb + else: + peaks_bw_array[rcu] = stop_sb - start_sb + peaks_sb_array[rcu] = (stop_sb + start_sb) / 2 + peaks_pwr_array[rcu] = peaks.get_peak_value(peaks_sb_array[rcu]) + + + x_rcus = data.rcus(band, 'x') + for x_rcu in x_rcus: + if data.mode(x_rcu) in (1, 2): + y_rcu = x_rcu - 1 + else: + y_rcu = x_rcu + 1 + x_value_trigger = False + x_down_trigger = False + y_value_trigger = False + y_down_trigger = False + thunderstorm = False + logger.debug("rcu=%d: X-top, sb=%d, pwr=%3.1fdB, bw=%d, mean-value-band=%3.1f" % ( + x_rcu, peaks_sb_array[x_rcu], peaks_pwr_array[x_rcu], peaks_bw_array[x_rcu], mean_pwr_array[x_rcu])) + logger.debug("rcu=%d: Y-top, sb=%d, pwr=%3.1fdB, bw=%d, mean-value-band=%3.1f" % ( + y_rcu, peaks_sb_array[y_rcu], peaks_pwr_array[y_rcu], peaks_bw_array[y_rcu], mean_pwr_array[y_rcu])) + + # if both polaritys not shifted and 1 pol is higher then average and the other is lower then average + if abs(peaks_sb_array[x_rcu] - median_max_sb) < 3 and abs(peaks_sb_array[y_rcu] - median_max_sb) < 3: + if (mean_pwr_array[x_rcu] < (median_subbands_pwr - 3.0) and mean_pwr_array[y_rcu] > (median_subbands_pwr + 3.0)): + thunderstorm = True + if (mean_pwr_array[y_rcu] < (median_subbands_pwr - 3.0) and mean_pwr_array[x_rcu] > (median_subbands_pwr + 3.0)): + thunderstorm = True + + if thunderstorm: + logger.debug("skip down test, probably thunderstorm active") + else: + if 66.0 < mean_pwr_array[x_rcu] < (median_subbands_pwr - 2.0): + logger.debug("rcu=%d: mean signal in test band for X lower than normal" % x_rcu) + x_value_trigger = True + if 66.0 < mean_pwr_array[y_rcu] < (median_subbands_pwr - 2.0): + logger.debug("rcu=%d: mean signal in test band for Y lower than normal" % y_rcu) + y_value_trigger = True + + if abs(peaks_sb_array[x_rcu] - median_max_peak_sb) > 10 or abs(median_bandwidth - peaks_bw_array[x_rcu]) > 10: + if peaks_bw_array[x_rcu] > 3: + logger.debug("rcu=%d: X broken or antenna down" % x_rcu) + x_down_trigger = True + if abs(peaks_sb_array[y_rcu] - median_max_peak_sb) > 10 or abs(median_bandwidth - peaks_bw_array[y_rcu]) > 10: + if peaks_bw_array[y_rcu] > 3: + logger.debug("rcu=%d: Y broken or antenna down" % y_rcu) + y_down_trigger = True + + if (x_value_trigger and x_down_trigger) or (y_value_trigger and y_down_trigger): + down_info.append((x_rcu, peaks_sb_array[x_rcu], peaks_pwr_array[x_rcu], peaks_bw_array[x_rcu], mean_pwr_array[x_rcu])) + down_info.append((y_rcu, peaks_sb_array[y_rcu], peaks_pwr_array[y_rcu], peaks_bw_array[x_rcu], mean_pwr_array[y_rcu])) + else: + if (peaks_bw_array[x_rcu] > 20) and (abs(peaks_sb_array[x_rcu] - median_max_peak_sb) > 10): + logger.debug("rcu=%d: X-top shifted normal=%d, now=%d" % ( + x_rcu, median_max_peak_sb, peaks_sb_array[x_rcu])) + shifted_info.append((x_rcu, peaks_sb_array[x_rcu], median_max_peak_sb)) + if (peaks_bw_array[y_rcu] > 20) and (abs(peaks_sb_array[y_rcu] - median_max_peak_sb) > 10): + logger.debug("rcu=%d: Y-top shifted normal=%d, now=%d" % ( + y_rcu, median_max_peak_sb, peaks_sb_array[y_rcu])) + shifted_info.append((y_rcu, peaks_sb_array[y_rcu], median_max_peak_sb)) + + + # if more than half the antennes are down or shifted, skip test + if len(down_info) > (_data.shape[0] / 2): + down_info = list() + if len(shifted_info) > (_data.shape[0] / 2): + shifted_info = list() + return down_info, shifted_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/flat.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/flat.py new file mode 100644 index 0000000000000000000000000000000000000000..58d63bbc2af459e96e0e01b51e97fd55b67a4a93 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/flat.py @@ -0,0 +1,24 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.fla..') +logger.debug("init logger") + +def check_for_flat(data, band, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_pwr = parset.as_float('flat.mean-pwr.min') + max_pwr = parset.as_float('flat.mean-pwr.max') + + _data = data.mean_spectras(freq_band=band, polarity='xy', masked=True) + flat_info = list() + for data_nr, rcu in enumerate(data.rcus(band, 'xy')): + mean_signal = ma.mean(_data[data_nr, :]) + if min_pwr < mean_signal < max_pwr: + logger.info("rcu=%d: cable probable off" % rcu) + flat_info.append((rcu, mean_signal)) + return flat_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/noise.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/noise.py new file mode 100644 index 0000000000000000000000000000000000000000..d6a7d1f51bf70cffa63d68b9c040780a52438f28 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/noise.py @@ -0,0 +1,109 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.noi..') +logger.debug("init logger") + +def check_for_noise(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + low_deviation = parset.as_float('noise.negative-deviation') + high_deviation = parset.as_float('noise.positive-deviation') + max_diff = parset.as_float('noise.max-difference') + passband = parset.as_int_list('noise.passband') + if passband is None: + logger.warning("no passband found, use default 1:511") + passband = range(1,512,1) + data.set_passband(band, passband) + + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + #logger.info("data shape %s" %(str(_data.shape))) + high_info = list() + low_info = list() + jitter_info = list() + + ref_value = float(ma.median(_data)) + # loop over rcus + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d data_nr=%d" %(rcu, data_nr)) + data_nr_value = float(ma.median(_data[data_nr, :, :])) + if data_nr_value < (ref_value + low_deviation): + logger.debug("data_nr=%d: masked, low signal, ref=%5.3f val=%5.3f" % (data_nr, ref_value, data_nr_value)) + low_info.append((rcu, _data[data_nr, :, :].min(), -1, (ref_value+low_deviation), + (_data[data_nr, :, :].max() - _data[data_nr, :, :].min()))) + data.mask_rcu(rcu) + spec_median = ma.median(_data, axis=2) + spec_max = spec_median.max(axis=1) + spec_min = spec_median.min(axis=1) + ref_value = float(ma.median(_data)) + ref_diff = float(ma.median(spec_max) - ma.median(spec_min)) + ref_std = float(ma.std(spec_median)) + # high_limit = ref_value + min(max((ref_std * 3.0),0.75), high_deviation) + high_limit = ref_value + max((ref_std * 3.0), high_deviation) + low_limit = ref_value + min((ref_std * -3.0), low_deviation) + n_secs = _data.shape[1] + logger.debug("median-signal=%5.3fdB, median-fluctuation=%5.3fdB, std=%5.3f, high_limit=%5.3fdB low_limit=%5.3fdB" % ( + ref_value, ref_diff, ref_std, high_limit, low_limit)) + # loop over rcus + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d data_nr=%d" %(rcu, data_nr)) + peaks = SearchPeak(_data[data_nr, 0, :]) + if not peaks.valid_data: + return low_info, high_info, jitter_info + peaks.search(delta=10.0) + if peaks.n_max_peaks() >= 30: + logger.debug("rcu=%d: found %d peaks, skip noise test" % (rcu, peaks.n_max_peaks())) + else: + n_bad_high_secs = 0 + n_bad_low_secs = 0 + if _data.shape[1] == 1: + n_bad_high_secs = 1 + n_bad_low_secs = 1 + + data_nr_max_diff = spec_max[data_nr] - spec_min[data_nr] + # logger.debug("data_nr_max_diff %f" %(data_nr_max_diff)) + # loop over secs + for val in spec_median[data_nr, :]: + # logger.debug("data_nr=%d: high-noise value=%5.3fdB max-ref-value=%5.3fdB" %(data_nr, val, ref_value)) + if val > high_limit: + n_bad_high_secs += 1 + + if val < low_limit: + n_bad_low_secs += 1 + + if n_bad_high_secs > 1: + high_info.append((rcu, spec_max[data_nr], n_bad_high_secs, high_limit, data_nr_max_diff)) + logger.debug("rcu=%d: max-noise=%5.3f %d of %d seconds bad" % ( + rcu, spec_max[data_nr], n_bad_high_secs, n_secs)) + + if n_bad_low_secs > 1: + low_info.append((rcu, spec_min[data_nr], n_bad_low_secs, low_limit, data_nr_max_diff)) + logger.debug("rcu=%d: min-noise=%5.3f %d of %d seconds bad" % ( + rcu, spec_min[data_nr], n_bad_low_secs, n_secs)) + + if (n_bad_high_secs == 0) and (n_bad_low_secs == 0): + max_cnt = 0 + min_cnt = 0 + if data_nr_max_diff > (ref_diff + max_diff): + check_high_value = ref_value + (ref_diff / 2.0) + check_low_value = ref_value - (ref_diff / 2.0) + for val in spec_median[data_nr, :]: + if val > check_high_value: + max_cnt += 1 + if val < check_low_value: + min_cnt += 1 + + # minimal 20% of the values must be out of the check band + secs = _data.shape[1] + if max_cnt > (secs * 0.10) and min_cnt > (secs * 0.10): + n_bad_jitter_secs = max_cnt + min_cnt + jitter_info.append((rcu, data_nr_max_diff, ref_diff, n_bad_jitter_secs)) + logger.debug("rcu=%d: max spectrum fluctuation %5.3f dB" % (rcu, data_nr_max_diff)) + + data.reset_masked_sb(band) + return low_info, high_info, jitter_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/oscillation.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/oscillation.py new file mode 100644 index 0000000000000000000000000000000000000000..424d703297d93508b1674f6e531dc1b2fb91e040 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/oscillation.py @@ -0,0 +1,68 @@ +import logging +from peakslib import * + + +logger = logging.getLogger('main.chk.osc..') +logger.debug("init logger") + + +def check_for_oscillation(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_peak_pwr = parset.as_float('oscillation.min-peak-pwr') + passband = parset.as_int_list('oscillation.passband') + if passband is None: + logger.warning("no passband found, use default 1:511") + passband = range(1,512,1) + data.set_passband(band, passband) + + info = list() + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + mean_spectras = ma.mean(_data, axis=1) + mean_spectra = ma.mean(mean_spectras, axis=0) + mean_low = ma.mean(_data.min(axis=1)) + info.append((-1, 0, 0, 0)) + + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) + peaks = SearchPeak(mean_spectras[data_nr, :] - mean_spectra) + if peaks.valid_data: + peaks.search(delta=min_peak_pwr, min_width=2, max_width=8) + max_val = mean_spectras[data_nr, :].max() + max_n_peaks = peaks.n_max_peaks() + max_sum_peaks = peaks.get_sum_peaks() + + bin_low = _data[data_nr, :, :].min(axis=0).mean() + + logger.debug("rcu=%d: number-of-peaks=%d max_value=%3.1f peaks_sum=%5.3f low_value=%3.1f" % ( + rcu, max_n_peaks, max_val, max_sum_peaks, bin_low)) + + out_of_band_peaks = 0 + for peak_sb, min_sb, max_sb in peaks.max_peaks: + if peak_sb in range(0, 25, 1): + out_of_band_peaks += 1 + if peak_sb in range(488, 512, 1): + out_of_band_peaks += 1 + if out_of_band_peaks >= 2: + logger.debug("detected out of band peaks") + # info.append((rcu, max_sum_peaks, max_n_peaks, bin_low)) + continue + + if max_n_peaks > 5: + if bin_low > (mean_low + 2.0): # peaks.getSumPeaks() > (median_sum_peaks * 2.0): + logger.debug("detected peaks in complete band") + info.append((rcu, max_sum_peaks, max_n_peaks, bin_low)) + continue + + if max_val > 150.0: # only one high peek + logger.debug("detected peak > 150 dB") + info.append((rcu, max_sum_peaks, max_n_peaks, bin_low)) + continue + + data.reset_masked_sb(band) + return info # (sorted(info,reverse=True)) diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/peakslib.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/peakslib.py new file mode 100644 index 0000000000000000000000000000000000000000..21221b21544cb54bf36d9cddca8202803ce606f1 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/peakslib.py @@ -0,0 +1,149 @@ + +from numpy import ma, fft, power, arange, isscalar, NaN, Inf, zeros +from sys import exit +import logging +from checkhardware_lib.data import * + +logger = logging.getLogger('main.chk.pea..') +logger.debug("init logger") + +class SearchPeak(object): + """ + search for all peaks (min & max) in spectra + """ + def __init__(self, data): + self.valid_data = False + if len(data.shape) == 1: + self.valid_data = True + self.data = data.copy() + self.n_data = len(data) + self.max_peaks = [] + self.min_peaks = [] + return + + def search(self, delta, min_width=1, max_width=100, skip_list=()): + self.max_peaks = [] + self.min_peaks = [] + + x = arange(0, len(self.data), 1) + + if not isscalar(delta): + exit('argument delta must be a scalar') + + if delta <= 0: + exit('argument delta must be positive') + + maxval, minval = -200, 200 + maxpos, minpos = 1, 1 + + lookformax = True + + # add subband to skiplist + skiplist = [] + if len(skip_list) > 0: + for max_pos, min_sb, max_sb in skip_list: + for sb in range(min_sb, max_sb+1, 1): + skiplist.append(sb) + + # skip subband 0 (always high signal) + for i in range(1, self.n_data, 1): + # sleep(0.001) + if ma.count_masked(self.data) > 1 and self.data.mask[i] is True: + continue + + now = self.data[i] + if now > maxval: + maxval = now + maxpos = x[i] + + if now < minval: + minval = now + minpos = x[i] + + if lookformax: + if now < (maxval - delta): + if maxpos not in skiplist: + peakwidth, min_sb, max_sb = self.get_peak_width(maxpos, delta) + # logger.debug("maxpos=%d, width=%d" %(maxpos, peakwidth)) + if min_width < peakwidth < max_width: + self.max_peaks.append([maxpos, min_sb, max_sb]) + minval = now + minpos = x[i] + lookformax = False + else: + if now > (minval + delta): + if minpos not in skiplist: + self.min_peaks.append([minpos, minpos, minpos]) + maxval = now + maxpos = x[i] + lookformax = True + + # if no peak found with the given delta, return maximum found + if len(self.max_peaks) == 0: + self.max_peaks.append([-1, -1, -1]) + return + + # return data[nr] + def get_peak_value(self, nr): + try: + return self.data[nr] + except IndexError: + return NaN + except: + raise + + def get_peak_width(self, nr, delta): + peakval = self.data[nr] + minnr = nr + maxnr = nr + for sb in range(nr, 0, -1): + if self.data[sb] < peakval: + minnr = sb + if self.data[sb] <= (peakval - delta): + break + for sb in range(nr, self.data.shape[0], 1): + if self.data[sb] < peakval: + maxnr = sb + if self.data[sb] <= (peakval - delta): + break + return maxnr-minnr, minnr, maxnr + + # return value and subband nr + def get_max_peak(self, sb_list=None): + maxval = 0.0 + minsb = 0.0 + maxsb = 0.0 + binnr = -1 + if sb_list is None: + check_range = range(512) + else: + check_range = sb_list + for peak, min_sb, max_sb in self.max_peaks: + if peak not in check_range: + continue + if self.data[peak] > maxval: + maxval = self.data[peak] + binnr = peak + minsb = min_sb + maxsb = max_sb + return maxval, binnr, minsb, maxsb + + def get_sum_peaks(self): + peaksum = 0.0 + for peak, min_sb, max_sb in self.max_peaks: + peaksum += self.data[peak] + return peaksum + + # return value and sbband nr + def get_min_peak(self): + minval = Inf + nr_bin = -1 + + for peak in self.min_peaks: + if self.data[peak] < minval: + minval = self.data[peak] + nr_bin = peak + return minval, nr_bin + + def n_max_peaks(self): + return len(self.max_peaks) diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/rf_power.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/rf_power.py new file mode 100644 index 0000000000000000000000000000000000000000..5593667d556b38fffa190baf38edf07cc32c58e1 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/rf_power.py @@ -0,0 +1,82 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.rf_..') +logger.debug("init logger") + + +def check_rf_power(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + #logger.debug('parset= %s' % str(parset)) + subbands = parset.as_int_list('rf.subbands') + min_signal = parset.as_float('rf.min-sb-pwr') + low_deviation = parset.as_float('rf.negative-deviation') + high_deviation = parset.as_float('rf.positive-deviation') + + logger.debug("band=%s pol=%s subband=%s" % ( + band, pol, str(subbands))) + logger.debug("min_signal=%5.1fdB low_deviation=%5.1fdB high_deviation=%5.1fdB" % ( + min_signal, low_deviation, high_deviation)) + + signal_info = dict() + test_info = dict() + test_sb = None + + # _data = (rcus x secs x subbands), all data for given band and pol + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + # 1 spectra with median from all rcu spectra (max from all seconds) + median_spectra = ma.median(ma.max(_data, axis=1), axis=0) + + if len(subbands) > 1: + peaks = SearchPeak(median_spectra) + peaks.search(delta=6.0, min_width=2) + maxval, binnr, minsb, maxsb = peaks.get_max_peak(sb_list=subbands) + if maxval >= min_signal: + test_sb = binnr + else: + test_sb = subbands[0] + + if not test_sb: + test_info['valid'] = False + test_info['subband'] = -1 + test_info['test_val'] = 0.0 + return test_info, signal_info + + test_sb_value = median_spectra[test_sb] + + test_info['subband'] = test_sb + test_info['test_val'] = test_sb_value + + # logger.debug("test_sb_value=%s min_signal=%s" % (str(test_sb_value), str(min_signal))) + if test_sb_value > min_signal: + test_info['valid'] = True + else: + test_info['valid'] = False + + # sb_data = rcus values, median value of total seconds + sb_data = ma.max(data.subbands(freq_band=band, polarity=pol, sb_set=test_sb, masked=True), axis=1) + rcu_list = data.rcus(band=band, polarity=pol) + logger.debug("used test_sb=%d" % test_sb) + #logger.debug("sb_data=%s" % str(sb_data)) + for data_nr, rcu in enumerate(rcu_list): + # logger.debug("data_nr=%d rcu=%d val=%5.1fdB" % (data_nr, rcu, sb_data[data_nr])) + if np.ma.is_masked(sb_data[data_nr]): + signal_info[str(rcu)] = {'value': 0.0, 'status': 'masked'} + elif sb_data[data_nr] < 2.0: + signal_info[str(rcu)] = {'value': sb_data[data_nr], 'status': 'no_signal'} + elif 55.0 < sb_data[data_nr] < 65.0: + signal_info[str(rcu)] = {'value': sb_data[data_nr], 'status': 'no_power'} + elif sb_data[data_nr] < (test_sb_value + low_deviation): + signal_info[str(rcu)] = {'value': sb_data[data_nr], 'status': 'low'} + elif sb_data[data_nr] > (test_sb_value + high_deviation): + signal_info[str(rcu)] = {'value': sb_data[data_nr], 'status': 'high'} + else: + signal_info[str(rcu)] = {'value': sb_data[data_nr], 'status': 'normal'} + + return test_info, signal_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/short.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/short.py new file mode 100644 index 0000000000000000000000000000000000000000..d2d7b6c59b7c65da350477895279e6589f018dfa --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/short.py @@ -0,0 +1,25 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.sho..') +logger.debug("init logger") + + +def check_for_short(data, band, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_pwr = parset.as_float('short.mean-pwr.min') + max_pwr = parset.as_float('short.mean-pwr.max') + + _data = data.mean_spectras(freq_band=band, polarity='xy', masked=True) + short_info = list() + for data_nr, rcu in enumerate(data.rcus(band, 'xy')): + mean_signal = ma.mean(_data[data_nr, :]) + if min_pwr < mean_signal < max_pwr: + logger.info("rcu=%d: cable shorted" % rcu) + short_info.append((rcu, mean_signal)) + return short_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/spurious.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/spurious.py new file mode 100644 index 0000000000000000000000000000000000000000..de4e67849eea44fc1806063abbe38d0b33bc3e36 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/spurious.py @@ -0,0 +1,64 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.spu..') +logger.debug("init logger") + + +def check_for_spurious(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_peak_pwr = parset.as_float('spurious.min-peak-pwr') + passband = parset.as_int_list('spurious.passband') + if passband is None: + logger.warning("no passband found, use default 1:511") + passband = range(1,512,1) + data.set_passband(band, passband) + + info = list() + + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + max_data = ma.max(_data, axis=1) + mean_data = ma.mean(_data, axis=1) + median_spec = ma.mean(max_data, axis=0) + peaks = SearchPeak(median_spec) + if not peaks.valid_data: + return info + + # first mask peaks available in all data + peaks.search(delta=(min_peak_pwr / 2.0)) # deta=20 for HBA + for peak, min_sb, max_sb in peaks.max_peaks: + peakwidth = max_sb - min_sb + if peakwidth > 8: + continue + min_sb = max(min_sb - 1, 0) + max_sb = min(max_sb + 1, peaks.n_data - 1) + logger.debug("mask sb %d..%d" % (min_sb, max_sb)) + for i in range(min_sb, max_sb, 1): + mean_data[:, i] = ma.masked + + # search in all data for spurious + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d data_nr=%d" % (rcu, data_nr)) + peaks = SearchPeak(mean_data[data_nr, :]) + if peaks.valid_data: + peaks.search(delta=min_peak_pwr) + for peak, min_sb, max_sb in peaks.max_peaks: + peakwidth = max_sb - min_sb + if peakwidth > 10: + continue + peak_val = peaks.get_peak_value(peak) + if peakwidth < 100 and peak_val != NaN: + logger.debug("rcu=%d: spurious, subband=%d..%d, peak=%3.1fdB" % ( + rcu, min_sb, max_sb, peak_val)) + if peaks.n_max_peaks() > 10: + # print data_nr, peaks.nMaxPeaks() + info.append(rcu) + + data.reset_masked_sb(band) + return info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/summator_noise.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/summator_noise.py new file mode 100644 index 0000000000000000000000000000000000000000..5c57d121b651fd6b9712e4647d989f901501e420 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/summator_noise.py @@ -0,0 +1,69 @@ +import logging +from peakslib import * + + +logger = logging.getLogger('main.chk.sum..') +logger.debug("init logger") + + +def check_for_summator_noise(data, band, pol, parset): + """ + :param data: recorded antenna data data + :param band: band to check + :param pol: polarity to check + :param parset: parameterset with check settings + :return: list with found failures + """ + min_peak_pwr = parset.as_float('summator-noise.min-peak-pwr') + passband = parset.as_int_list('summator-noise.passband') + if passband is None: + logger.warning("no passband found, use default 1:511") + passband = range(1,512,1) + data.set_passband(band, passband) + + sn_info = list() # summator noise + _data = data.spectras(freq_band=band, polarity=pol, masked=True) + + n_secs = _data.shape[1] + secs = (n_secs/2,n_secs-1) + + for data_nr, rcu in enumerate(data.rcus(band, pol)): + # logger.debug("rcu=%d data_nr=%d" %(rcu, data_nr)) + sum_sn_peaks = 0 + max_peaks = 0 + for sec in secs: + peaks_ref = SearchPeak(_data[:, sec, :].mean(axis=0)) + if peaks_ref.valid_data: + peaks_ref.search(delta=min_peak_pwr, min_width=3) + + peaks = SearchPeak(_data[data_nr, sec, :]) + if peaks.valid_data: + peaks.search(delta=min_peak_pwr, skip_list=peaks_ref.max_peaks) + n_peaks = peaks.n_max_peaks() + if n_peaks < 3: + continue + sn_peaks = 0 + last_sb, min_sb, max_sb = peaks.max_peaks[0] + last_sb_val = peaks.get_peak_value(last_sb) + for sb, min_sb, max_sb in peaks.max_peaks[1:]: + sb_val = peaks.get_peak_value(sb) + + sb_diff = sb - last_sb + sb_val_diff = sb_val - last_sb_val + if sb_diff in (3, 4): + if abs(sb_val_diff) < 2.0: + sn_peaks += 1 + elif sn_peaks < 6 and abs(sb_val_diff) > 3.0: + sn_peaks = 0 + last_sb = sb + last_sb_val = sb_val + + sum_sn_peaks += sn_peaks + max_peaks = max(max_peaks, n_peaks) + + if sum_sn_peaks > (len(secs) * 3.0): + sn_peaks = sum_sn_peaks / len(secs) + sn_info.append((rcu, sn_peaks, max_peaks)) + + data.reset_masked_sb(band) + return sn_info diff --git a/LCU/checkhardware/checkhardware_lib/spectrum_checks/tools.py b/LCU/checkhardware/checkhardware_lib/spectrum_checks/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..8ff28e2c701d1f6f6aa094c44410a21c46cc771c --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spectrum_checks/tools.py @@ -0,0 +1,19 @@ +import logging +from peakslib import * + +logger = logging.getLogger('main.chk.too..') +logger.debug("init logger") + +def psd(data, sampletime): + """ + :param data: data for fft + :param sampletime: used sampletime + :return: fft + """ + if data.ndim != 1: + return [], [] + fft_data = fft.fft(data) + n = fft_data.size + psd_freq = fft.fftfreq(n, sampletime) + _psd = power(abs(fft_data), 2) / n + return _psd[:n / 2], psd_freq[:n / 2] diff --git a/LCU/checkhardware/checkhardware_lib/spu.py b/LCU/checkhardware/checkhardware_lib/spu.py new file mode 100644 index 0000000000000000000000000000000000000000..43300ff085b0a85d7b290a54166a606be9bd51e5 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/spu.py @@ -0,0 +1,135 @@ +import logging +from lofar import * + +logger = logging.getLogger('main.spu') +logger.debug("starting spu logger") + +class SPU(object): + def __init__(self, db): + self.db = db + self.board_info_str = [] + self.board_info_val = [-1, 0.0, 0.0, 0.0, 0.0, 0] + + def extract_board_info(self, line): + li = line.split("|") + if not li[0].strip().isdigit(): + return False + + self.board_info_str = [i.strip() for i in li] + + if li[0].strip().isdigit(): + self.board_info_val[0] = int(li[0].strip()) + else: + self.board_info_val[0] = -1 + + for i in xrange(1, 5, 1): + if li[i].strip().replace('.', '').isdigit(): + self.board_info_val[i] = float(li[i].strip()) + else: + self.board_info_val[i] = 0.0 + + if li[5].strip().isdigit(): + self.board_info_val[5] = int(li[5].strip()) + else: + self.board_info_val[5] = 0 + return True + + def check_status(self, parset): + """ + check PSU if boards idle and fully loaded + """ + logger.info("=== SPU status check ===") + if not check_active_rspdriver(): + logger.warning("RSPDriver down, skip test") + return + + noload = [] + fullload_3 = [] + fullload_5 = [] + logger.debug("check spu no load") + answer = rspctl('--spustatus') + + # check if Driver is available + if answer.find('No Response') > 0: + logger.warning("No RSPDriver") + + else: + infolines = answer.splitlines() + for line in infolines: + if self.extract_board_info(line): + bi = self.board_info_val + noload.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) + self.db.spu[bi[0]].temp = bi[5] + bi = self.board_info_str + logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( + bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) + + # turn on all hbas + logger.debug("check spu full load mode 3") + rsp_rcu_mode(3, self.db.lbh.select_list()) + answer = rspctl('--spustatus') + infolines = answer.splitlines() + for line in infolines: + if self.extract_board_info(line): + bi = self.board_info_val + fullload_3.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) + bi = self.board_info_str + logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( + bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) + + # turn on all hbas + logger.debug("check spu full load mode 5") + rsp_rcu_mode(5, self.db.hba.select_list()) + answer = rspctl('--spustatus') + infolines = answer.splitlines() + for line in infolines: + if self.extract_board_info(line): + bi = self.board_info_val + fullload_5.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) + bi = self.board_info_str + logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( + bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) + + #logger.debug("noload=%s" % str(noload)) + #logger.debug("fullload_3=%s" % str(fullload_3)) + #logger.debug("fullload_5=%s" % str(fullload_5)) + for sr in range(self.db.nr_spu): + #logger.debug("sr=%d" % sr) + # calculate mean of noload, fullload_3, fullload_5 + self.db.spu[sr].rcu_5_0_volt = (noload[sr][1] + fullload_3[sr][1] + fullload_5[sr][1]) / 3.0 + self.db.spu[sr].lba_8_0_volt = fullload_3[sr][2] + self.db.spu[sr].hba_48_volt = fullload_5[sr][3] + self.db.spu[sr].spu_3_3_volt = (noload[sr][4] + fullload_3[sr][4] + fullload_5[sr][4]) / 3.0 + + if self.db.spu[sr].temp > parset.as_float('temperature.max'): + self.db.spu[sr].temp_ok = 0 + + if (not (parset.as_float('voltage.5_0.min') <= noload[sr][1] <= parset.as_float('voltage.5_0.max')) or + not (parset.as_float('voltage.5_0.min') < fullload_3[sr][1] <= parset.as_float('voltage.5_0.max')) or + not (parset.as_float('voltage.5_0.min') <= fullload_5[sr][1] <= parset.as_float('voltage.5_0.max')) or + (noload[sr][1] - fullload_3[sr][1]) > parset.as_float('voltage.5_0.max-drop')): + self.db.spu[sr].rcu_ok = 0 + logger.info("SPU voltage 5.0V out of range") + + if (not (parset.as_float('voltage.8_0.min') <= noload[sr][2] <= parset.as_float('voltage.8_0.max')) or + not (parset.as_float('voltage.8_0.min') <= fullload_3[sr][2] <= parset.as_float('voltage.8_0.max')) or + (noload[sr][2] - fullload_3[sr][2]) > parset.as_float('voltage.8_0.max-drop')): + self.db.spu[sr].lba_ok = 0 + logger.info("SPU voltage 8.0V out of range") + + if (not (parset.as_float('voltage.48_0.min') <= noload[sr][3] <= parset.as_float('voltage.48_0.max')) or + not (parset.as_float('voltage.48_0.min') <= fullload_5[sr][3] <= parset.as_float('voltage.48_0.max')) or + (noload[sr][3] - fullload_5[sr][3]) > parset.as_float('voltage.48_0.max-drop')): + self.db.spu[sr].hba_ok = 0 + logger.info("SPU voltage 48V out of range") + + if (not (parset.as_float('voltage.3_3.min') <= noload[sr][4] <= parset.as_float('voltage.3_3.max')) or + not (parset.as_float('voltage.3_3.min') <= fullload_3[sr][4] <= parset.as_float('voltage.3_3.max')) or + not (parset.as_float('voltage.3_3.min') <= fullload_5[sr][4] <= parset.as_float('voltage.3_3.max')) or + (noload[sr][4] - fullload_5[sr][4]) > parset.as_float('voltage.3_3.max-drop')): + self.db.spu[sr].spu_ok = 0 + logger.info("SPU voltage 3.3V out of range") + + logger.info("=== Done SPU check ===") + self.db.add_test_done('SPU') + return diff --git a/LCU/checkhardware/checkhardware_lib/tbb.py b/LCU/checkhardware/checkhardware_lib/tbb.py new file mode 100644 index 0000000000000000000000000000000000000000..1f4ac0bbb03c5ec7d802205202aa5903451bf940 --- /dev/null +++ b/LCU/checkhardware/checkhardware_lib/tbb.py @@ -0,0 +1,162 @@ +import logging +import numpy as np +from lofar import * + +logger = logging.getLogger('main.tbb') +logger.debug("starting tbb logger") + +# class for checking TBB boards using tbbctl +class TBB(object): + def __init__(self, db): + self.db = db + self.nr = self.db.nr_tbb + self.driverstate = True + #tbbctl('--free') + + # check software versions of driver, tbbctl and TP/MP firmware + def check_versions(self, parset): + logger.info("=== TBB Version check ===") + answer = tbbctl('--version') + + # check if Driver is available + if answer.find('TBBDriver is NOT responding') > 0: + logger.warning("No TBBDriver") + self.driverstate = False + else: + infolines = answer.splitlines() + info = infolines[4:6] + infolines[9:-1] + + # check if image_nr > 0 for all boards + if str(info).count('V') != (self.nr * 4): + logger.warning("WARNING, Not all boards in working image") + + for tbb in self.db.tbb: + board_info = info[2 + tbb.nr].strip().split(' ') + # print board_info + if board_info[3].split()[1] != parset.as_string('version.tp'): + logger.warning("Board %d Not right TP version" % tbb.nr) + tbb.tp_version = board_info[3].split()[1] + + if board_info[4].split()[1] != parset.as_string('version.mp'): + logger.warning("Board %d Not right MP version" % tbb.nr) + tbb.mp_version = board_info[4].split()[1] + logger.info("=== Done TBB Version check ===") + self.db.add_test_done('TV') + return + + # Check memory address and data lines + def check_memory(self): + logger.info("=== TBB Memory check ===") + tbbctl('--free') + for tbb in self.db.tbb: + if not tbb.board_active: + logger.info("Board %d ot active" % tbb.nr) + tbb.memory_ok = 0 + continue + answer = tbbctl('--testddr=%d' % tbb.nr) + info = answer.splitlines()[-3:] + memory_ok = True + if info[0].strip() != 'All Addresslines OK': + logger.info("Board %d Addresline error" % tbb.nr) + memory_ok = False + + if info[1].strip() != 'All Datalines OK': + logger.info("Board %d Datalines error" % tbb.nr) + memory_ok = False + + if not memory_ok: + tbb.memory_ok = 0 + logger.info(answer) + logger.info("=== Done TBB Memory check ===") + self.db.add_test_done('TM') + return + + def check_board(self, parset): + board_ok = True + logger.info("=== TBB Board check ===") + if not check_active_tbbdriver(): + logger.warning("TBBDriver down, skip test") + return False + answer = tbbctl('--status') + + mp_temp = np.zeros((self.db.nr_tbb, 4), float) + mp_temp[:,:] = -1 + + for line in answer.splitlines(): + if 'ETH' in line or 'clock' in line: + info = line.split() + else: + continue + #print info + try: + tbb_nr = int(info[0].strip()) + if not self.db.tbb[tbb_nr].board_active: + logger.info("Board %d ot active" % tbb_nr) + continue + if tbb_nr in range(12): + tbb = self.db.tbb[tbb_nr] + tbb.voltage1_2 = float(info[3][:-1]) + tbb.voltage2_5 = float(info[4][:-1]) + tbb.voltage3_3 = float(info[5][:-1]) + tbb.pcb_temp = float(info[6][:-2]) + tbb.tp_temp = float(info[7][:-2]) + tbb.mp0_temp = float(info[8][:-2]) + tbb.mp1_temp = float(info[10][:-2]) + tbb.mp2_temp = float(info[12][:-2]) + tbb.mp3_temp = float(info[14][:-2]) + mp_temp[tbb_nr, 0] = tbb.mp0_temp + mp_temp[tbb_nr, 1] = tbb.mp1_temp + mp_temp[tbb_nr, 2] = tbb.mp2_temp + mp_temp[tbb_nr, 3] = tbb.mp3_temp + + except ValueError: + logger.warning("value error in parse stage: %s" % line) + except IndexError: + logger.warning("index error in parse stage: %s" % line) + except: + raise + + mp_temp = np.ma.masked_less(mp_temp, 1.0) + mp_check_temp = np.ma.median(mp_temp[:,:]) + parset.as_float('temperature.mp.max_delta') + + for tbb in self.db.tbb: + logger.debug("TBB board %2d, voltages: 1.2V=%4.2f, 2.5V=%4.2f, 3.3V=%4.2f" % ( + tbb.nr, tbb.voltage1_2, tbb.voltage2_5, tbb.voltage3_3)) + + if not (parset.as_float('voltage.1_2.min') <= tbb.voltage1_2 <= parset.as_float('voltage.1_2.max')): + tbb.voltage_ok = 0 + logger.info("TBB board %2d, bad voltage 1.2V=%4.2fV" % (tbb.nr, tbb.voltage1_2)) + if not (parset.as_float('voltage.2_5.min') <= tbb.voltage2_5 <= parset.as_float('voltage.2_5.max')): + tbb.voltage_ok = 0 + logger.info("TBB board %2d, bad voltage 2.5V=%4.2fV" % (tbb.nr, tbb.voltage2_5)) + if not (parset.as_float('voltage.3_3.min') <= tbb.voltage3_3 <= parset.as_float('voltage.3_3.max')): + tbb.voltage_ok = 0 + logger.info("TBB board %2d bad voltage 3.3V=%4.2fV" % (tbb.nr, tbb.voltage3_3)) + + for tbb in self.db.tbb: + logger.debug( + "TBB board %2d, temperatures: pcb=%3.0f, tp=%3.0f, mp0=%3.0f, mp1=%3.0f, mp2=%3.0f, mp3=%3.0f" % ( + tbb.nr, tbb.pcb_temp, tbb.tp_temp, tbb.mp0_temp, tbb.mp1_temp, tbb.mp2_temp, tbb.mp3_temp)) + if tbb.pcb_temp > parset.as_float('temperature.max'): + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature pcb_temp=%3.0f" % (tbb.nr, tbb.pcb_temp)) + if tbb.tp_temp > parset.as_float('temperature.tp.max'): + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature tp_temp=%3.0f" % (tbb.nr, tbb.tp_temp)) + if tbb.mp0_temp > parset.as_float('temperature.mp.max') or tbb.mp0_temp > mp_check_temp: + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature mp0_temp=%3.0f" % (tbb.nr, tbb.mp0_temp)) + if tbb.mp1_temp > parset.as_float('temperature.mp.max') or tbb.mp1_temp > mp_check_temp: + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature mp1_temp=%3.0f" % (tbb.nr, tbb.mp1_temp)) + if tbb.mp2_temp > parset.as_float('temperature.mp.max') or tbb.mp2_temp > mp_check_temp: + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature mp2_temp=%3.0f" % (tbb.nr, tbb.mp2_temp)) + if tbb.mp3_temp > parset.as_float('temperature.mp.max') or tbb.mp3_temp > mp_check_temp: + tbb.temp_ok = 0 + logger.info("TBB board %2d, high temperature mp3_temp=%3.0f" % (tbb.nr, tbb.mp3_temp)) + logger.debug("mp check temperature= %3.1f" % mp_check_temp) + logger.info("=== Done TBB Board check ===") + self.db.add_test_done('TBC') + return board_ok + # end of cTBB class diff --git a/LCU/checkhardware/doStationTest.sh b/LCU/checkhardware/do_station_test.sh similarity index 63% rename from LCU/checkhardware/doStationTest.sh rename to LCU/checkhardware/do_station_test.sh index 5e58022ea93d67496125a448c0b3091fea7d850a..bb49098a4b21dcf478752499d5219109c888cea0 100755 --- a/LCU/checkhardware/doStationTest.sh +++ b/LCU/checkhardware/do_station_test.sh @@ -1,5 +1,5 @@ #!/bin/bash - + # default values START="" @@ -27,7 +27,7 @@ done if [ $HELP == "yes" ] then echo "Usage:" - echo " doStationTest.sh -s 20130624_04:00:00 -e 20130624_06:00:00 -u" + echo " do_station_test.sh -s 20130624_04:00:00 -e 20130624_06:00:00 -u" echo " -s : start time" echo " -e : end time" echo " -u : update pvss" @@ -38,18 +38,21 @@ then exit fi -host=`hostname -s` +hostname=`hostname -s` +host=`echo "$hostname" | awk '{ print toupper($1) }'` + +cd /opt/stationtest/ # set filenames and dirs -locallogdir="/opt/stationtest/data/" -globallogdir="/globalhome/log/stationtest/" +local_data_dir="/opt/stationtest/data/" +global_data_dir="/globalhome/log/stationtest/" if [ $LEVEL -ne 0 ] then SERVICE="no" fi -filenameNow=$host"_StationTest.csv" +filenameNow=$host"_station_test.csv" if [ $SERVICE == "yes" ] then LEVEL=2 @@ -81,9 +84,9 @@ fi # Check hardware if [ $SERVICE == "yes" ] then - checkHardware.py $level $start $stop -ls=info + ./check_hardware.py $level $start $stop -ls=info else - checkHardware.py $level $start $stop + ./check_hardware.py $level $start $stop fi err=$? @@ -95,25 +98,25 @@ then #new settings by Wilfred, 9-7-2013 if [ $UPDATE == "yes" ] then - updatePVSS.py -N=5,50,3 -J=5,50,3 -E -S=10 -LBLN=5,50,3 -LBLJ=5,50,3 -LBLS=10 -LBHN=5,50,3 -LBHJ=5,50,3 -LBHS=10 + ./update_pvss.py -N=5,50,3 -J=5,50,3 -E -S=10 -LBLN=5,50,3 -LBLJ=5,50,3 -LBLS=10 -LBHN=5,50,3 -LBHJ=5,50,3 -LBHS=10 else - updatePVSS.py -no_update -N=5,50,3 -J=5,50,3 -E -S=10 -LBLN=5,50,3 -LBLJ=5,50,3 -LBLS=10 -LBHN=5,50,3 -LBHJ=5,50,3 -LBHS=10 + ./update_pvss.py -no_update -N=5,50,3 -J=5,50,3 -E -S=10 -LBLN=5,50,3 -LBLJ=5,50,3 -LBLS=10 -LBHN=5,50,3 -LBHJ=5,50,3 -LBHS=10 fi - + # Copy to local filename file in local dir - cp $locallogdir$filenameNow $locallogdir$filenameLocal - + cp $local_data_dir$filenameNow $local_data_dir$filenameLocal + # Add to history - cat $locallogdir$filenameNow >> $locallogdir$filenameLocalHistory + cat $local_data_dir$filenameNow >> $local_data_dir$filenameLocalHistory # Copy from local to global dir - cp $locallogdir$filenameLocal $globallogdir - cp $locallogdir$filenameLocalHistory $globallogdir - cp $locallogdir$filenameBadRCUs $globallogdir + cp $local_data_dir$filenameLocal $global_data_dir + cp $local_data_dir$filenameLocalHistory $global_data_dir + cp $local_data_dir$filenameBadRCUs $global_data_dir fi if [ $SERVICE == "yes" ] then # Show last results on screen - showTestResult.py -f=$locallogdir$filenameNow + show_test_result.py -f=$local_data_dir$filenameNow fi diff --git a/LCU/checkhardware/lib/data_lib.py b/LCU/checkhardware/lib/data_lib.py deleted file mode 100644 index 9a802045a634b0576165b8d6e62e31bc3cc744e1..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/data_lib.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# data lib - -from general_lib import * -from lofar_lib import * -from search_lib import * -import os -import numpy as np -import logging -from time import sleep - -test_version = '0815' - -logger = None -def init_data_lib(): - global logger - logger = logging.getLogger() - logger.debug("init logger data_lib") - -# get and return recorded data in various ways -class cRCUdata: - global logger - def __init__(self, n_rcus, minvalue=1): - self.n_rcus = n_rcus - self.minvalue = minvalue - self.active_rcus = range(self.n_rcus) - self.subband_mask = [] # subband_mask selects subbands to block - self.active_rcus_changed = False - self.reset() - self.reset_masks() - - def reset(self): - self.ssData = np.ones((self.n_rcus, 1, 512), np.float64) - self.testSignal_X = -1.0 - self.testSubband_X = 0 - self.testSignal_Y = -1.0 - self.testSubband_Y = 0 - self.frames = 0 - self.clock = 200.0 - self.rec_time = 0 - - def reset_masks(self): - logger.debug('reset rcu mask') - self.rcu_mask = [] # rcu_mask selects rcus to block - - def getRecTime(self): - return self.rec_time - - def setActiveRcus(self, rcus): - self.active_rcus = rcus - - def setInActiveRcu(self, rcu): - logger.debug('delete rcu %d from active-rcu list' % rcu) - if rcu in self.active_rcus: - self.active_rcus.remove(rcu) - self.add_to_rcu_mask(rcu) - self.active_rcus_changed = True - - def getActiveRcus(self, pol='XY'): - rcus = [] - for i in self.active_rcus: - if pol in ('X', 'x') and i % 2 == 0: - rcus.append(i) - if pol in ('Y', 'y') and i % 2 == 1: - rcus.append(i) - if pol in ('XY', 'xy'): - rcus.append(i) - return (rcus) - - def isActiveRcusChanged(self): - return self.active_rcus_changed - - def resetActiveRcusChanged(self): - self.active_rcus_changed = False - - def add_to_rcu_mask(self, rcu): - if rcu not in self.rcu_mask: - self.rcu_mask.append(rcu) - logger.debug('bad-rcu-mask=%s' % str(sorted(self.rcu_mask))) - - def setMask(self, blocked_subbands): - self.subband_mask = blocked_subbands - - def getMask(self): - return (self.subband_mask) - - def record(self, rec_time=2, read=True, slow=False): - removeAllDataFiles() - self.reset() - self.rec_time = rec_time - - if slow == True: - x_list = [] - y_list = [] - for i in sorted(self.active_rcus): - if i % 2 == 0: - x_list.append(i) - else: - y_list.append(i) - rcus = selectStr(x_list) - logger.debug("Wait %d seconds while recording X data" %(rec_time)) - rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' %(rec_time, dataDir(), rcus), wait=0.0) - - rcus = selectStr(y_list) - logger.debug("Wait %d seconds while recording Y data" %(rec_time)) - rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' %(rec_time, dataDir(), rcus), wait=0.0) - else: - rcus = selectStr(self.active_rcus) - logger.debug("Wait %d seconds while recording XY data" %(rec_time)) - rspctl('--statistics --duration=%d --integration=1 --directory=%s --select=%s' %(rec_time, dataDir(), rcus), wait=0.0) - - if read == True: - self.readFiles() - - def readFile(self, full_filename): - sleep(0.02) - data = np.fromfile(full_filename, dtype=np.float64) - n_samples = len(data) - if (n_samples % 512) > 0: - logger.warn("data error: number of samples (%d) not multiple of 512 in '%f'" %(n_samples, full_filename)) - self.frames = n_samples / 512 - data = data.reshape(self.frames,512) - #logger.info("recorded data shape %s" %(str(data.shape))) - return (data) - - def readFiles(self): - files_in_dir = sorted(os.listdir(dataDir())) - if len(files_in_dir) == 0: - logger.warn('No data recorded !!') - self.reset() - return - data_shape = self.readFile(os.path.join(dataDir(),files_in_dir[0])).shape - ssdata = np.zeros((self.n_rcus, data_shape[0],data_shape[1]), dtype=np.float64) - for file_name in files_in_dir: - #path, filename = os.split(file_name) - rcu = int(file_name.split('.')[0][-3:]) - ssdata[rcu,:,:] = self.readFile(os.path.join(dataDir(),file_name)) - #logger.debug("%s rcu=%d" %(file_name, rcu)) - # mask zero values and convert to dBm - self.ssData = np.log10(np.ma.masked_less(ssdata, self.minvalue)) * 10.0 - # do not use subband 0 - self.ssData[:,:,0] = np.ma.masked - - # subbands is list to mask - def getMaskedData(self): - data = self.ssData.copy() - for sb in self.subband_mask: - data[:,:,sb] = np.ma.masked - for rcu in range(self.n_rcus): - if rcu in self.rcu_mask: - data[rcu,:,:] = np.ma.masked - return (data) - - def getMeanSpectra(self, pol='XY'): - if pol in ('XY', 'xy'): - return (self.ssData[:,:,:].mean(axis=1).mean(axis=0)) - if pol in (0, 'X', 'x'): - return (self.ssData[0::2,:,:].mean(axis=1).mean(axis=0)) - if pol in (1, 'Y', 'y'): - return (self.ssData[1::2,:,:].mean(axis=1).mean(axis=0)) - - def getMedianSpectra(self, pol='XY'): - if pol in ('XY', 'xy'): - return (np.median(np.mean(self.ssData, axis=1), axis=0)) - if pol in (0, 'X', 'x'): - return (np.median(np.mean(self.ssData[0::2,:,:], axis=1), axis=0)) - if pol in (1, 'Y', 'y'): - return (np.median(np.mean(self.ssData[1::2,:,:], axis=1), axis=0)) - - def getSpectra(self, rcu): - return(np.mean(self.ssData[rcu,:,:], axis=0)) - - def getSubbands(self, rcu): - return (self.getMaskedData()[int(rcu),:,:].mean(axis=0)) - - def getSubbandX(self, subband=None): - if subband is None: - return (self.getMaskedData()[0::2,:,self.testSubband_Y].mean(axis=1)) - return (self.getMaskedData()[0::2,:,subband].mean(axis=1)) - - def getSubbandY(self, subband=None): - if subband is None: - return (self.getMaskedData()[1::2,:,self.testSubband_Y].mean(axis=1)) - return (self.getMaskedData()[1::2,:,subband].mean(axis=1)) - - def getAll(self, pol='XY'): - if pol in ('XY', 'xy'): - return (self.getMaskedData()) - if pol in ('x','X',0): - return (self.getMaskedData()[0::2,:,:]) - if pol in ('y','Y',1): - return (self.getMaskedData()[1::2,:,:]) - - def getAllX(self): - return (self.getMaskedData()[0::2,:,:]) - - def getAllY(self): - return (self.getMaskedData()[1::2,:,:]) - - def getMedianRcu(self, rcu): - return(np.ma.median(self.getMaskedData()[int(rcu),:,:].mean(axis=0))) - - def searchTestSignal(self, subband=-1, minsignal=75.0, maxsignal=100.0): - # ss = median for all band over all rcu's - # forget subband 0 - ssX = np.ma.median(self.getMaskedData()[::2,:,:].mean(axis=1),axis=0) - ssY = np.ma.median(self.getMaskedData()[1::2,:,:].mean(axis=1),axis=0) - - if subband != -1: - if ssX[subband] > minsignal and ssY[subband] > minsignal: - self.testSignal_X = ssX[subband] - self.testSubband_X = subband - self.testSignal_Y = ssY[subband] - self.testSubband_Y = subband - return - else: - logger.debug("Test signal on subband %d not strong enough X=%3.1fdB Y=%3.1fdB" %(subband, ssX[subband], ssY[subband])) - - # no subband given or not in requested range, look for better - for i in range(0,ssX.shape[0],1): - if ssX[i] > minsignal and ssX[i] < maxsignal and ssX[i] > self.testSignal_X: - self.testSignal_X = ssX[i] - self.testSubband_X = i - if ssY[i] > minsignal and ssY[i] < maxsignal and ssY[i] > self.testSignal_Y: - self.testSignal_Y = ssY[i] - self.testSubband_Y = i - return -#### end of cRCUdata class #### diff --git a/LCU/checkhardware/lib/general_lib.py b/LCU/checkhardware/lib/general_lib.py deleted file mode 100644 index 36d5de786fa43c96f3aee911706c3715f2c3a582..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/general_lib.py +++ /dev/null @@ -1,126 +0,0 @@ -r""" -general script -""" - -from subprocess import (Popen, PIPE) -import traceback -import time -import os -import sys -import logging - -general_version = '0913' -logger = logging.getLogger() - -def writeMessage(msg): - sendCmd('wall', msg) - return - -# Return date string in the following format YYYYMMDD -def getShortDateStr(tm=time.gmtime()): - return (time.strftime("%Y%m%d", tm)) - -# Return time string in the following format HH:MM:SS -def getDateStr(tm=time.gmtime()): - return (time.strftime("%d-%m-%Y", tm)) - -# Return time string in the following format HH:MM:SS -def getTimeStr(tm=time.gmtime()): - return (time.strftime("%H:%M:%S", tm)) -# Return time string in the following format HH:MM:SS -def getDateTimeStr(tm=time.gmtime()): - return (time.strftime("%d-%m-%YT%H:%M:%S", tm)) - -# Run cmd with args and return response -def sendCmd(cmd='', args=''): - if cmd != '': - try: - args = args.replace(' =','=').replace('= ','=') - cmdList = [cmd] + args.split() - #print cmdList - cmdline = Popen(cmdList, stdout=PIPE, stderr=PIPE ) - (so, se) = cmdline.communicate() - if len(so) != 0: - return (so) - else: - return ('Error, %s' % se) - except: - logger.error('Caught %s', str(sys.exc_info()[0])) - logger.error(str(sys.exc_info()[1])) - logger.error('TRACEBACK:\n%s', traceback.format_exc()) - return ('Exception Error') - - return ('') - -# Get Host name -def getHostName(): - try: - host = sendCmd('hostname', '-s') - if host == 'Exception Error': - host = 'Unknown' - except: - host = 'Unknown' - return (host.strip()) - -# file logger -class cLogger: - def __init__(self, logdir, filename, screenPrefix=''): - self.fullFilename = os.path.join(logdir,filename) - self.logfile = open(self.fullFilename, 'w') - self.prefix = screenPrefix - self.starttime = time.time() - - def __del__(self): - self.logfile.close() - - def getFullFileName(self): - return (self.fullFilename) - - def resetStartTime(self, screen=False): - self.starttime = time.time() - self.info("Start time %s" %(time.strftime("%H:%M:%S", time.gmtime(self.starttime))), screen=screen) - - def printBusyTime(self, screen=False): - self.info("Time from start %s" %(time.strftime("%H:%M:%S", (time.gmtime(time.time() - self.starttime)))), screen=screen) - - def printTimeNow(self, screen=False): - self.info("Time %s" %(time.strftime("%H:%M:%S", time.gmtime(time.time()))), screen=screen) - - def info(self, msg, noEnd=False, screen=False): - if len(msg) != 0: - if screen: - print self.prefix+' '+msg - if noEnd == False: - msg += '\n' - self.logfile.write(msg) - self.logfile.flush() - -class cTestLogger(cLogger): - def __init__(self, logdir): - filename = getHostName() + "_StationTest" + '.csv' - cLogger.__init__(self, logdir, filename) - - def addLine(self, info): - cLogger.info(self, info) - -class cStationLogger(cLogger): - def __init__(self, logdir, filetime=time.gmtime()): - filename = "stationtest_" + getHostName() + '.log' - cLogger.__init__(self, logdir, filename) - cLogger.info(self, "StID >: %s" %(getHostName())) - cLogger.info(self, "Lgfl >: %s" %(os.path.join(logdir,filename))) - testdate = time.strftime("%a, %d %b %Y %H:%M:%S", filetime) - cLogger.info(self, "Time >: %s" %(testdate)) - - def addLine(self, info): - cLogger.info(self, info) - - -class cPVSSLogger(cLogger): - def __init__(self, logdir): - filename = getHostName() + "_StationTest_PVSS" + '.log' - cLogger.__init__(self, logdir, filename) - #cLogger.info(self, "# PVSS input file") - - def addLine(self, info): - cLogger.info(self, info) diff --git a/LCU/checkhardware/lib/lofar_lib.py b/LCU/checkhardware/lib/lofar_lib.py deleted file mode 100644 index 28f503c8cc933bb091b9ca6e5ee2ae137dc2e60e..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/lofar_lib.py +++ /dev/null @@ -1,510 +0,0 @@ -# lofar_lib - -import os -import sys -import time -import logging -import socket -import struct -import string -from general_lib import sendCmd - -os.umask(001) -lofar_version = '0514' - -CoreStations = ('CS001C','CS002C','CS003C','CS004C','CS005C','CS006C','CS007C','CS011C',\ - 'CS013C','CS017C','CS021C','CS024C','CS026C','CS028C','CS030C','CS031C',\ - 'CS032C','CS101C','CS103C','CS201C','CS301C','CS302C','CS401C','CS501C') - -RemoteStations = ('RS106C','RS205C','RS208C','RS210C','RS305C','RS306C','RS307C','RS310C',\ - 'RS406C','RS407C','RS409C','RS503C','RS508C','RS509C') - -InternationalStations = ('DE601C','DE602C','DE603C','DE604C','DE605C','FR606C','SE607C','UK608C') - - -StationType = dict( CS=1, RS=2, IS=3 ) - -logger = None -rcumode = -1 -active_delay_str = ('555,'*16)[:-1] - - -def init_lofar_lib(): - global logger - logger = logging.getLogger() - logger.debug("init logger lofar_lib") - if os.access(dataDir(), os.F_OK): - removeAllDataFiles() - else: - os.mkdir(dataDir()) - - -def dataDir(): - return (r'/localhome/stationtest/sb_data') - -# remove all *.dat -def removeAllDataFiles(): - if os.access(dataDir(), os.F_OK): - files = os.listdir(dataDir()) - #print files - for f in files: - if f[-3:] == 'dat' or f[-3:] == 'nfo': - os.remove(os.path.join(dataDir(),f)) - - -# return station type -def getStationType(StID): - if StID in CoreStations: - return (StationType[CS]) - if StID in RemoteStations: - return (StationType[RS]) - if StID in InternationalStations: - return (StationType[IS]) - - -# read from RemoteStation.conf file number of RSP and TB Boards -""" -# -# THIS FILE IS GENERATED, DO NOT MODIFY IT. -# -# RemoteStation.conf for CS002 -# -# Describes the amount of available hardware on the station. -# - -RS.STATION_ID = 2 -RS.N_RSPBOARDS = 12 -RS.N_TBBOARDS = 6 -RS.N_LBAS = 96 -RS.N_HBAS = 48 -RS.HBA_SPLIT = Yes -RS.WIDE_LBAS = Yes -""" -def readStationConfig(): - f = open('/opt/lofar/etc/RemoteStation.conf', 'r') - lines = f.readlines() - f.close() - - ID = nRSP = nTBB = nLBA = nLBL = nLBH = nHBA = HBA_SPLIT = 0 - - for line in lines: - if (line[0] == '#') or (len(line) < 2): - continue - key, val = line.split('=') - key = key.strip() - val = val.strip() - if key == "RS.STATION_ID": - ID = int(val) - continue - if key == "RS.N_RSPBOARDS": - nRSP = int(val) - continue - if key == "RS.N_TBBOARDS": - nTBB = int(val) - continue - if key == "RS.N_LBAS": - nLBA = int(val) - if nLBA == nRSP * 8: - nLBL = nLBA / 2 - nLBH = nLBA / 2 - else: - nLBL = 0 - nLBH = nLBA - continue - if key == "RS.N_HBAS": - nHBA = int(val) - continue - if key == "RS.HBA_SPLIT": - if string.upper(val) == "YES": - HBA_SPLIT = 1 - continue - return(ID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT) - - -# [lofarsys@RS306C stationtest]$ swlevel 2 -# Going to level 2 -# Starting RSPDriver -# Loading image 4 on RSPboard 0 ... -# Loading image 4 on RSPboard 1 ... -# Loading image 4 on RSPboard 2 ... -# Loading image 4 on RSPboard 3 ... -# Loading image 4 on RSPboard 4 ... -# Loading image 4 on RSPboard 5 ... -# Loading image 4 on RSPboard 6 ... -# Loading image 4 on RSPboard 7 ... -# RSPboard 8: Error requesting active firmware version (communication error) -# Loading image 4 on RSPboard 9 ... -# Loading image 4 on RSPboard 10 ... -# Loading image 4 on RSPboard 11 ... -# One or more boards have a communication problem; try reset the 48V -# root 21470 1 1 10:41 pts/2 00:00:00 /opt/lofar/bin/RSPDriver -# Starting TBBDriver -# root 21492 1 0 10:41 pts/2 00:00:00 /opt/lofar/bin/TBBDriver -# -# Status of all software level: -# 1 : PVSS00pmon 16177 -# 1 : SoftwareMonitor 16227 -# 1 : LogProcessor 16248 -# 1 : ServiceBroker 16278 -# 1 : SASGateway 16299 -# --- -# 2 : RSPDriver 21470 -# 2 : TBBDriver 21492 -# --- -# 3 : CalServer DOWN -# 3 : BeamServer DOWN -# --- -# 4 : HardwareMonitor DOWN -# --- -# 5 : SHMInfoServer DOWN -# --- -# 6 : CTStartDaemon DOWN -# 6 : StationControl DOWN -# 6 : ClockControl DOWN -# 6 : CalibrationControl DOWN -# 6 : BeamControl DOWN -# 6 : TBBControl DOWN -# --- - -def swlevel(level=None): - _level = level - board_errors = list() - if (level != None): - if _level < 0: - _level *= -1 - answer = sendCmd('swlevel', str(_level)) - else: - answer = sendCmd('swlevel') - - current_level = 0 - for line in answer.splitlines(): - if line.find("Going to level") > -1: - current_level = int(line.split()[-1]) - - elif line.find("Currently set level") > -1: - current_level = int(line.split()[-1]) - if current_level < 0: - logger.warn("Current swlevel is %d" %(current_level)) - if line.find("Error requesting active firmware version") > -1: - endpos = line.find(":") - board_errors.append(int(line[:endpos].split()[1])) - logger.warn(line) - return (current_level, board_errors) - -def reset48V(): - logger.info("Try to reset 48V power") - ec_name = socket.gethostname()[:-1]+"ec" - ec_ip = socket.gethostbyname(ec_name) - logger.debug("EC to connect = %s" %(ec_ip)) - - connected = False - - try: - sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - except socket.error: - sck.close() - return - - try: - sck.settimeout(4.0) - sck.connect((ec_ip, 10000)) - connected = True - time.sleep(0.5) - cmd = struct.pack('hhh', 22, 0, 0) - logger.debug("send cmd") - sck.send(cmd) - sck.settimeout(4.0) - logger.debug("recv cmd") - data = sck.recv(6) - sck.close() - logger.debug("reset done") - except socket.error: - print "ec socket connect error" - sck.close() - - -# Run rspctl command with given args and return response -def rspctl(args='', wait=0.0): - if args != '': - logger.debug("rspctl %s" %(args)) - response = sendCmd('rspctl', args) - if wait > 0.0: - time.sleep(wait) - return (response) - return ('No args given') - - -# Run tbbctl command with given args and return response -def tbbctl(args=''): - if args != '': - logger.debug("tbbctl %s" %(args)) - return (sendCmd('tbbctl', args)) - return ('No args given') - -def checkActiveTBBDriver(): - answer = sendCmd('swlevel').strip().splitlines() - for line in answer: - if line.find('TBBDriver') > -1: - if line.find('DOWN') != -1: - return (False) - return (True) - -# wait until all boards have a working image loaded -# returns 1 if ready or 0 if timed_out -def waitTBBready(n_boards=6): - timeout = 90 - logger.info("wait for working TBB boards ") - sys.stdout.flush() - while timeout > 0: - answer = tbbctl('--version') - #print answer - if answer.find('TBBDriver is NOT responding') > 0: - if timeout < 10: - logger.info("TBBDriver is NOT responding, try again in every 5 seconds") - time.sleep(5.0) - timeout -= 5 - if timeout < 60: - return (0) - continue - # check if image_nr > 0 for all boards - if answer.count('V') == (n_boards * 4): - logger.info("All boards in working image") - return (1) - time.sleep(1.0) - timeout -= 1 - logger.warn("Not all TB boards in working image") - return (0) - -def checkActiveRSPDriver(): - answer = sendCmd('swlevel').strip().splitlines() - for line in answer: - if line.find('RSPDriver') > -1: - if line.find('DOWN') != -1: - return (False) - return (True) - -# wait until all boards have a working image loaded -# returns 1 if ready or 0 if timed_out -# -# [lofarsys@RS306C ~]$ rspctl --version -# RSP[ 0] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 1] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 2] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 3] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 4] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 5] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 6] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 7] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 8] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[ 9] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[10] RSP version = 0, BP version = 0.0, AP version = 0.0 -# RSP[11] RSP version = 0, BP version = 0.0, AP version = 0.0 - -def waitRSPready(): - timeout = 60 - logger.info("wait for working RSP boards ") - sys.stdout.flush() - while timeout > 0: - answer = rspctl('--version') - #print answer - if answer.count('No Response') > 0: - time.sleep(5.0) - timeout -= 5 - if timeout < 60: - return (0) - continue - # check if image_nr > 0 for all boards - if answer.count('0.0') == 0: - logger.info("All boards in working image") - return (1) - else: - logger.warn("Not all RSP boards in working image") - logger.debug(answer) - time.sleep(5.0) - timeout -= 1 - return (0) - -# convert select-list to select-string -def selectStr(sel_list): - last_sel = -2 - set = False - select = "" - for sel in sorted(sel_list): - if sel == last_sel+1: - set = True - else: - if set: - set = False - select += ':%d' %(last_sel) - select += ",%d" %(sel) - last_sel = sel - if set: - select += ':%d' %(last_sel) - return (select[1:]) - - # convert select-string to sel_list -def extractSelectStr(selectStr): - selectStr = selectStr.strip() + '.' - if selectStr.strip() == '.': - return([]) - sel_list = list() - num_str = '' - num = -1 - set_num = -1 - for ch in selectStr: - if ch.isalnum(): - num_str += ch - continue - num_str = num_str.strip() - num = int(num_str) - num_str = '' - - if set_num > -1: - while (set_num < num): - sel_list.append(set_num) - set_num += 1 - set_num = -1 - - if ch == ',': - sel_list.append(num) - - if ch == ':': - set_num = num - - if num > -1: - sel_list.append(num) - return (sorted(sel_list)) - -def getClock(): - answer = rspctl("--clock") - #print answer[-6:-3] - clock = float(answer[-7:-4]) - return (clock) - -# function used for antenna testing -def swap_xy(state): - if state in (0,1): - if state == 1: - logger.info("XY-output swapped") - else: - logger.info("XY-output normal") - rspctl('--swapxy=%d' %(state)) - -def resetRSPsettings(): - if rspctl ('--clock').find('200MHz') < 0: - rspctl ('--clock=200') - logger.info ("Changed Clock to 200MHz") - time.sleep (2.0) - rspctl('--wg=0', wait=0.0) - rspctl('--rcuprsg=0', wait=0.0) - rspctl('--datastream=0', wait=0.0) - rspctl('--splitter=0', wait=0.0) - rspctl('--specinv=0', wait=0.0) - rspctl('--bitmode=16', wait=0.0) - rspctl('--rcumode=0', wait=0.0) - rspctl('--rcuenable=0', wait=0.0) - #rspctl ('--hbadelays=%s' %(('128,'*16)[:-1]), wait=8.0) - -def turnonRCUs(mode, rcus): - #global rcumode - #start_mode = rcumode - select = selectStr(rcus) - logger.info("turn RCU's on, mode %d" %(mode)) - logger.info("enable rcus") - rspctl('--rcuenable=1 --select=%s' %(select), wait=0.0) - logger.info("setweights") - rspctl('--aweights=8000,0', wait=0.0) - - if mode == 5: - rspctl('--specinv=1', wait=0.0) - else: - rspctl('--specinv=0', wait=0.0) - - if mode < 3: - swap_xy(state=1) - else: - swap_xy(state=0) - logger.info("set rcu mode") - rsp_rcu_mode(mode, rcus) - #rcumode = mode - return - -def turnoffRCUs(): - global rcumode - logger.info("RCU's off, mode 0") - rspctl('--rcumode=0', wait=0.0) - rspctl('--rcuenable=0', wait=0.0) - rspctl('--aweights=0,0', wait=1.0) - rcumode = 0 - -# set rcu mode, if mode > 4(hba) turn on hba's in steps to avoid power dips -def rsp_rcu_mode(mode, rcus): - global rcumode - if mode > 0 and mode < 5: # lba modes - rcumode = mode - select = selectStr(rcus) - rspctl('--rcumode=%d --select=%s' %(mode, select), wait=6.0) - return (0) - elif mode < 8: # hba modes - rcumode = mode - # maximum 12 power RCUs each step - steps = int(round(len(rcus) / 24.)) - for step in range(0,(steps*2),2): - rculist = sorted(rcus[step::(steps*2)]+rcus[step+1::(steps*2)]) - select = string.join(list([str(rcu) for rcu in rculist]),',') - rspctl('--rcumode=%d --select=%s' %(mode, select), wait=2.0) - time.sleep(6.0) - return (0) - else: - return (-1) - -# set hba_delays in steps to avoid power dips, and discharge if needed -def rsp_hba_delay(delay, rcus, discharge=True): - global active_delay_str - - if delay == active_delay_str: - logger.debug("requested delay already active, skip hbadelay command") - return (1) - - if discharge == True: - # count number of elements off in last command - n_hba_off = 0 - for i in active_delay_str.split(','): - if int(i,10) & 0x02: - n_hba_off += 1 - - # count number of elements on in new command, and make discharge string - n_hba_on = 0 - if n_hba_off > 0: - discharge_str = '' - for i in delay.split(','): - if int(i,10) & 0x02: - discharge_str += "2," - else: - discharge_str += "0," - n_hba_on += 1 - - # discharge if needed - if n_hba_off > 0 and n_hba_on > 0: - logger.info("set hbadelays to 0 for 1 second") - if n_hba_on > 2: - steps = int(round(len(rcus) / 24.)) - logger.debug("send hbadelay command in %d steps" %(steps)) - for step in range(0,(steps*2),2): - rculist = sorted(rcus[step::(steps*2)]+rcus[step+1::(steps*2)]) - select = string.join(list([str(rcu) for rcu in rculist]),',') - rspctl('--hbadelay=%s --select=%s' %(discharge_str[:-1], select), wait=1.0) - rspctl('--hbadelay=%s --select=%s' %(discharge_str[:-1], select), wait=1.0) - time.sleep(3.0) - else: - rspctl('--hbadelay=%s' %(discharge_str[:-1]), wait=1.0) - rspctl('--hbadelay=%s' %(discharge_str[:-1]), wait=4.0) - - logger.debug("send hbadelay command") - rspctl('--hbadelay=%s' %(delay), wait=1.0) - rspctl('--hbadelay=%s' %(delay), wait=4.0) - - active_delay_str = delay - return (0) - - diff --git a/LCU/checkhardware/lib/search_lib.py b/LCU/checkhardware/lib/search_lib.py deleted file mode 100644 index 85d80dd232928f597afa9c6c02de7c7dfc181d5f..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/search_lib.py +++ /dev/null @@ -1,498 +0,0 @@ -#!/usr/bin/python - -""" -library with all search functions - -P.Donker ASTRON -""" -from numpy import ma, fft, power, arange, asarray, isscalar, NaN, Inf, zeros -from sys import exit -import logging -from time import sleep - -search_version = '0415' - -logger = logging.getLogger() - -""" -search for all peaks (min & max) in spectra -""" -class cSearchPeak: - def __init__(self, data): - self.valid_data = False - if len(data.shape) == 1: - self.valid_data = True - self.data = data.copy() - self.n_data = len(data) - self.max_peaks = [] - self.min_peaks = [] - return - - def search(self, delta, max_width=100, skip_list=[]): - self.max_peaks = [] - self.min_peaks = [] - - x = arange(0,len(self.data),1) - - if not isscalar(delta): - exit('argument delta must be a scalar') - - if delta <= 0: - exit('argument delta must be positive') - - maxval, minval = self.data[1], self.data[1] - maxpos, minpos = 1, 1 - - lookformax = True - - # add subband to skiplist - skiplist = [] - if len(skip_list) > 0: - for max_pos, min_sb, max_sb in skip_list: - for sb in range(min_sb,max_sb+1,1): - skiplist.append(sb) - - # skip subband 0 (always high signal) - for i in range(1,self.n_data,1): - #sleep(0.001) - if ma.count_masked(self.data) > 1 and self.data.mask[i] == True: - continue - - now = self.data[i] - if now > maxval: - maxval = now - maxpos = x[i] - - if now < minval: - minval = now - minpos = x[i] - - if lookformax: - if now < (maxval - delta): - if maxpos not in skiplist: - peakwidth, min_sb, max_sb = self.getPeakWidth(maxpos, delta) - #logger.debug("maxpos=%d, width=%d" %(maxpos, peakwidth)) - if (peakwidth < max_width): - self.max_peaks.append([maxpos, min_sb, max_sb]) - minval = now - minpos = x[i] - lookformax = False - else: - if now > (minval + delta): - if minpos not in skiplist: - self.min_peaks.append(minpos) - maxval = now - maxpos = x[i] - lookformax = True - - # if no peak found with the given delta, return maximum found - if len(self.max_peaks) == 0: - self.max_peaks.append([maxpos, maxpos, maxpos]) - return - - # return data[nr] - def getPeakValue(self, nr): - try: - return (self.data[nr]) - except: - return (NaN) - - def getPeakWidth(self, nr, delta): - peakval = self.data[nr] - minnr = nr - maxnr = nr - for sb in range(nr,0,-1): - if self.data[sb] < peakval: - minnr = sb - if self.data[sb] <= (peakval - delta): - break - for sb in range(nr,self.data.shape[0],1): - if self.data[sb] < peakval: - maxnr = sb - if self.data[sb] <= (peakval - delta): - break - - return (maxnr-minnr, minnr, maxnr) - - # return value and subband nr - def getMaxPeak(self): - maxval = 0.0 - minsb = 0.0 - maxsb = 0.0 - binnr = -1 - - for peak, min_sb, max_sb in self.max_peaks: - if self.data[peak] > maxval: - maxval = self.data[peak] - binnr = peak - minsb = min_sb - maxsb = max_sb - return (maxval, binnr, minsb, maxsb) - - def getSumPeaks(self): - peaksum = 0.0 - for peak, min_sb, max_sb in self.max_peaks: - peaksum += self.data[peak] - return (peaksum) - - # return value and sbband nr - def getMinPeak(self): - minval = Inf - nr_bin = -1 - - for peak in self.min_peaks: - if self.data[peak] < minval: - minval = self.data[peak] - nr_bin = peak - return (minval, nr_bin) - - def nMaxPeaks(self): - return (len(self.max_peaks)) - -# return psd off data and freq bins -def PSD(data, sampletime): - if data.ndim != 1: - return ([],[]) - fft_data = fft.fft(data) - n = fft_data.size - psd_freq = fft.fftfreq(n, sampletime) - psd = power(abs(fft_data),2)/n - return (psd[:n/2], psd_freq[:n/2]) - -def searchFlat(data): - _data = data.getAll().mean(axis=1) - flat_info = list() - for rcu in data.active_rcus: - mean_signal = ma.mean(_data[rcu,:]) - if mean_signal > 61.0 and mean_signal < 64.5: - logger.info("rcu=%d: cable probable off" %(rcu)) - flat_info.append((rcu, mean_signal)) - return(flat_info) - -def searchShort(data): - _data = data.getAll().mean(axis=1) - short_info = list() - for rcu in data.active_rcus: - mean_signal = ma.mean(_data[rcu,:]) - if mean_signal > 55.0 and mean_signal < 61.0: - logger.info("rcu=%d: cable shorted" %(rcu)) - short_info.append((rcu, mean_signal)) - return(short_info) - -def searchDown(data, subband): - _data = data.getAll().mean(axis=1) - down_info = list() - shifted_info = list() - start_sb = subband - 70 - stop_sb = subband + 70 - delta = 3 - - peaks = cSearchPeak(ma.median(_data[:,start_sb:stop_sb], axis=0)) - if not peaks.valid_data: - return (down_info, shifted_info) - - peaks.search(delta=delta) - (median_max_val, median_max_sb, min_sb, max_sb) = peaks.getMaxPeak() - median_bandwidth = max_sb - min_sb - #peakwidth, min_sb, max_sb = peaks.getPeakWidth(median_max_sb, delta) - median_max_sb += start_sb - - median_value = ma.median(_data[:,start_sb:stop_sb]) - logger.debug("reference peak in band %d .. %d : subband=%d, bw(3dB)=%d, median-value-band=%3.1fdB" %\ - (start_sb, stop_sb, (median_max_sb), median_bandwidth, median_value)) - - peaks_array = zeros((_data.shape[0]),'i') - peaks_bw_array = zeros((_data.shape[0]),'i') - - for rcu in data.active_rcus: - peaks = cSearchPeak(_data[rcu,start_sb:stop_sb]) - if peaks.valid_data: - peaks.search(delta=delta) - (maxpeak_val, maxpeak_sb, min_sb, max_sb) = peaks.getMaxPeak() - - if maxpeak_sb > 0: - #peakwidth, min_sb, max_sb = peaks.getPeakWidth(maxpeak_sb, delta) - peaks_bw_array[rcu] = max_sb - min_sb - peaks_array[rcu] = start_sb + maxpeak_sb - else: - peaks_bw_array[rcu] = stop_sb - start_sb - peaks_array[rcu] = subband - - for ant in range(_data.shape[0]/2): - x_rcu = ant * 2 - y_rcu = x_rcu + 1 - - mean_val_trigger = False - down_trigger = False - - ant_x_mean_value = ma.mean(_data[x_rcu,start_sb:stop_sb]) - ant_y_mean_value = ma.mean(_data[y_rcu,start_sb:stop_sb]) - - logger.debug("rcu=%d/%d: X-top, sb%d, bw=%d, mean-value-band=%3.1f Y-top, sb%d, bw=%d, mean-value-band=%3.1f" %\ - (x_rcu, y_rcu, peaks_array[x_rcu], peaks_bw_array[x_rcu], ant_x_mean_value, peaks_array[y_rcu], peaks_bw_array[y_rcu], ant_y_mean_value)) - - if (((ant_x_mean_value < (median_value - 3.0)) and (ant_x_mean_value > 66)) or - ((ant_y_mean_value < (median_value - 3.0)) and (ant_y_mean_value > 66))): - logger.debug("rcu_bin=%d/%d: mean signal in test band for X and Y lower than normal" %(x_rcu, y_rcu)) - mean_val_trigger = True - - if ((((abs(peaks_array[x_rcu] - median_max_sb) > 10) or (abs(median_bandwidth - peaks_bw_array[x_rcu]) > 10)) and (peaks_bw_array[x_rcu] > 3)) or - (((abs(peaks_array[y_rcu] - median_max_sb) > 10) or (abs(median_bandwidth - peaks_bw_array[y_rcu]) > 10)) and (peaks_bw_array[y_rcu] > 3))): - logger.debug("rcu=%d/%d: antenna probable down" %(x_rcu, y_rcu)) - down_trigger = True - - if mean_val_trigger and down_trigger: - down_info.append((ant, peaks_array[x_rcu], peaks_array[y_rcu], median_max_sb)) - - if not down_trigger: - if ((peaks_bw_array[x_rcu] > 20) and (abs(peaks_array[x_rcu] - median_max_sb) > 10)): - logger.debug("rcu=%d: X-top shifted normal=%d, now=%d" %(x_rcu, median_max_sb, peaks_array[x_rcu])) - shifted_info.append((x_rcu, peaks_array[x_rcu], median_max_sb)) - - if ((peaks_bw_array[y_rcu] > 20) and (abs(peaks_array[y_rcu] - median_max_sb) > 10)): - logger.debug("rcu=%d: Y-top shifted normal=%d, now=%d" %(y_rcu, median_max_sb, peaks_array[y_rcu])) - shifted_info.append((y_rcu, peaks_array[y_rcu], median_max_sb)) - - # if more than half de antennes down or shifted, skip test - if len(down_info) > (_data.shape[0] / 2): - down_info = list() - if len(shifted_info) > (_data.shape[0] / 2): - shifted_info = list() - return (down_info, shifted_info) - - -# find oscillation -def search_oscillation(data, pol, delta): - info = list() - _data = data.getAll(pol=pol) - mean_spectras = ma.mean(_data, axis=1) - mean_spectra = ma.mean(mean_spectras, axis=0) - mean_low = ma.mean(_data.min(axis=1)) - info.append((-1, 0, 0, 0)) - - for rcu in data.getActiveRcus(pol): - rcu_bin = rcu - if pol not in ('XY', 'xy'): - rcu_bin /= 2 - #logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) - #max_peak_val = 0 - max_n_peaks = 0 - max_sum_peaks = 0 - peaks = cSearchPeak(mean_spectras[rcu_bin,:] - mean_spectra) - if peaks.valid_data: - peaks.search(delta=delta, max_width=8) - max_val = mean_spectras[rcu_bin,:].max() - max_n_peaks = peaks.nMaxPeaks() - max_sum_peaks = peaks.getSumPeaks() - - bin_low = _data[rcu_bin,:,:].min(axis=0).mean() - if max_n_peaks > 5: - logger.debug("rcu_bin=%d: number-of-peaks=%d max_value=%3.1f peaks_sum=%5.3f low_value=%3.1f" %\ - (rcu_bin, max_n_peaks, max_val, max_sum_peaks, bin_low)) - if bin_low > (mean_low + 2.0): #peaks.getSumPeaks() > (median_sum_peaks * 2.0): - info.append((rcu_bin, max_sum_peaks, max_n_peaks, bin_low)) - - if max_val > 150.0: # only one high peek - info.append((rcu_bin, max_sum_peaks, max_n_peaks, bin_low)) - - return (info) #(sorted(info,reverse=True)) - -# find summator noise -def search_summator_noise(data, pol, min_peak): - sn_info = list() # summator noise - cr_info = list() # cable reflection - _data = data.getAll(pol=pol) - - secs = _data.shape[1] - for rcu in sorted(data.getActiveRcus(pol)): - rcu_bin = rcu - if pol not in ('XY', 'xy'): - rcu_bin /= 2 - #logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) - sum_sn_peaks = 0 - sum_cr_peaks = 0 - max_peaks = 0 - for sec in range(secs): - peaks_ref = cSearchPeak(_data[:,sec,:].mean(axis=0)) - if peaks_ref.valid_data: - peaks_ref.search(delta=min_peak) - - peaks = cSearchPeak(_data[rcu_bin,sec,:]) - if peaks.valid_data: - peaks.search(delta=min_peak, skip_list=peaks_ref.max_peaks) - n_peaks = peaks.nMaxPeaks() - if n_peaks < 3: - continue - sn_peaks = 0 - cr_peaks = 0 - last_sb, min_sb, max_sb = peaks.max_peaks[0] - last_sb_val = peaks.getPeakValue(last_sb) - for sb, min_sb, max_sb in peaks.max_peaks[1:]: - sb_val = peaks.getPeakValue(sb) - - sb_diff = sb - last_sb - sb_val_diff = sb_val - last_sb_val - if sb_diff in (3,4): - if abs(sb_val_diff) < 2.0: - sn_peaks += 1 - elif sn_peaks < 6 and abs(sb_val_diff) > 3.0: - sn_peaks = 0 - if sb_diff in (6,7): - if abs(sb_val_diff) < 2.0: - cr_peaks += 1 - elif cr_peaks < 6 and abs(sb_val_diff) > 3.0: - cr_peaks = 0 - last_sb = sb - last_sb_val = sb_val - - sum_sn_peaks += sn_peaks - sum_cr_peaks += cr_peaks - max_peaks = max(max_peaks, n_peaks) - - if (sum_sn_peaks > (secs * 3.0)): - sn_peaks = sum_sn_peaks / secs - sn_info.append((rcu_bin, sn_peaks, max_peaks)) - if sum_cr_peaks > (secs * 3.0): - cr_peaks = sum_cr_peaks / secs - cr_info.append((rcu_bin, cr_peaks, max_peaks)) - return (sn_info, cr_info) - - -# find noise -# noise looks like the noise floor is going up and down -# -# kijk ook naar op en neer gaande gemiddelden -def search_noise(data, pol, low_deviation, high_deviation, max_diff): - _data = data.getAll(pol=pol) - high_info = list() - low_info = list() - jitter_info = list() - - ref_value = ma.median(_data) - # loop over rcus - for rcu in sorted(data.getActiveRcus(pol)): - rcu_bin = rcu - if pol not in ('XY', 'xy'): - rcu_bin /= 2 - #logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) - rcu_bin_value = ma.median(_data[rcu_bin,:,:]) - if rcu_bin_value < (ref_value + low_deviation): - logger.debug("rcu_bin=%d: masked, low signal, ref=%5.3f val=%5.3f" %(rcu_bin, ref_value, rcu_bin_value)) - low_info.append((rcu_bin, _data[rcu_bin,:,:].min(), -1 , (ref_value+low_deviation), (_data[rcu_bin,:,:].max() - _data[rcu_bin,:,:].min()))) - _data[rcu_bin,:,:] = ma.masked - spec_median = ma.median(_data, axis=2) - spec_max = spec_median.max(axis=1) - spec_min = spec_median.min(axis=1) - ref_value = ma.median(_data) - ref_diff = ma.median(spec_max) - ma.median(spec_min) - ref_std = ma.std(spec_median) - #high_limit = ref_value + min(max((ref_std * 3.0),0.75), high_deviation) - high_limit = ref_value + max((ref_std * 3.0), high_deviation) - low_limit = ref_value + min((ref_std * -3.0), low_deviation) - n_secs = _data.shape[1] - logger.debug("median-signal=%5.3fdB, median-fluctuation=%5.3fdB, std=%5.3f, high_limit=%5.3fdB low_limit=%5.3fdB" %\ - (ref_value, ref_diff, ref_std, high_limit, low_limit)) - # loop over rcus - for rcu in sorted(data.getActiveRcus(pol)): - rcu_bin = rcu - if pol not in ('XY', 'xy'): - rcu_bin /= 2 - #logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) - peaks = cSearchPeak(_data[rcu_bin,0,:]) - if not peaks.valid_data: - return (low_info, high_info, jitter_info) - peaks.search(delta=10.0) - if peaks.nMaxPeaks() >= 30: - logger.debug("rcu_bin=%d: found %d peaks, skip noise test" %(rcu_bin, peaks.nMaxPeaks())) - else: - n_bad_high_secs = 0 - n_bad_low_secs = 0 - if _data.shape[1] == 1: - n_bad_high_secs = 1 - n_bad_low_secs = 1 - n_bad_jitter_secs = 0 - - rcu_bin_max_diff = spec_max[rcu_bin] - spec_min[rcu_bin] - #logger.debug("rcu_bin_max_diff %f" %(rcu_bin_max_diff)) - # loop over secs - for val in spec_median[rcu_bin,:]: - #logger.debug("rcu_bin=%d: high-noise value=%5.3fdB max-ref-value=%5.3fdB" %(rcu_bin, val, ref_value)) - if (val > high_limit): - n_bad_high_secs += 1 - - if (val < low_limit): - n_bad_low_secs += 1 - - if n_bad_high_secs > 1: - high_info.append((rcu_bin, spec_max[rcu_bin], n_bad_high_secs, high_limit, rcu_bin_max_diff)) - logger.debug("rcu_bin=%d: max-noise=%5.3f %d of %d seconds bad" %(rcu_bin, spec_max[rcu_bin], n_bad_high_secs, n_secs)) - - if n_bad_low_secs > 1: - low_info.append((rcu_bin, spec_min[rcu_bin], n_bad_low_secs , low_limit, rcu_bin_max_diff)) - logger.debug("rcu_bin=%d: min-noise=%5.3f %d of %d seconds bad" %(rcu_bin, spec_min[rcu_bin], n_bad_low_secs, n_secs)) - - if (n_bad_high_secs == 0) and (n_bad_low_secs == 0): - max_cnt = 0 - min_cnt = 0 - if rcu_bin_max_diff > (ref_diff + max_diff): - check_high_value = ref_value + (ref_diff / 2.0) - check_low_value = ref_value - (ref_diff / 2.0) - for val in spec_median[rcu_bin,:]: - if val > check_high_value: - max_cnt += 1 - if val < check_low_value: - min_cnt += 1 - - # minimal 20% of the values must be out of the check band - secs = _data.shape[1] - if max_cnt > (secs * 0.10) and min_cnt > (secs * 0.10): - n_bad_jitter_secs = max_cnt + min_cnt - jitter_info.append((rcu_bin, rcu_bin_max_diff, ref_diff, n_bad_jitter_secs)) - logger.debug("rcu_bin=%d: max spectrum fluctuation %5.3f dB" %(rcu_bin, rcu_bin_max_diff)) - return (low_info, high_info, jitter_info) - -# find spurious around normal signals -# -def search_spurious(data, pol, delta): - info = list() - _data = data.getAll(pol=pol) - max_data = ma.max(_data, axis=1) - mean_data = ma.mean(_data, axis=1) - median_spec = ma.mean(max_data, axis=0) - peaks = cSearchPeak(median_spec) - if not peaks.valid_data: - return (info) - - # first mask peaks available in all data - peaks.search(delta=(delta/2.0)) #deta=20 for HBA - for peak, min_sb, max_sb in peaks.max_peaks: - peakwidth = max_sb - min_sb - if peakwidth > 8: - continue - min_sb = max(min_sb-1, 0) - max_sb = min(max_sb+1, peaks.n_data-1) - logger.debug("mask sb %d..%d" %(min_sb, max_sb)) - for i in range(min_sb, max_sb, 1): - mean_data[:,i] = ma.masked - - # search in all data for spurious - for rcu in sorted(data.getActiveRcus(pol)): - rcu_bin = rcu - if pol not in ('XY', 'xy'): - rcu_bin /= 2 - logger.debug("rcu=%d rcu_bin=%d" %(rcu, rcu_bin)) - peaks = cSearchPeak(mean_data[rcu_bin,:]) - if peaks.valid_data: - peaks.search(delta=delta) - for peak, min_sb, max_sb in peaks.max_peaks: - peakwidth = max_sb - min_sb - if peakwidth > 10: - continue - peak_val = peaks.getPeakValue(peak) - if peakwidth < 100 and peak_val != NaN: - logger.debug("rcu_bin=%d: spurious, subband=%d..%d, peak=%3.1fdB" %(rcu_bin, min_sb, max_sb, peak_val)) - if peaks.nMaxPeaks() > 10: - #print rcu_bin, peaks.nMaxPeaks() - info.append(rcu_bin) - return(info) - diff --git a/LCU/checkhardware/lib/test_db.py b/LCU/checkhardware/lib/test_db.py deleted file mode 100644 index ec2ee679766728e2701ba7fd0e72e44253261b56..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/test_db.py +++ /dev/null @@ -1,883 +0,0 @@ -#!/usr/bin/python - -from copy import deepcopy -from general_lib import * -from lofar_lib import * -import time -import logging -import string - -db_version = '0415' - -logger = None -def init_test_db(): - global logger - logger = logging.getLogger() - logger.debug("init logger test_db") - -class cDB: - def __init__(self, StID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT): - self.StID = StID - self.nr_rsp = nRSP - self.nr_spu = nRSP / 4 - self.nr_rcu = nRSP * 8 - self.nr_lbl = nLBL - self.nr_lbh = nLBH - self.nr_hba = nHBA - self.hba_split = HBA_SPLIT - self.nr_tbb = nTBB - - self.script_versions = '' - - self.board_errors = list() - self.rcumode = -1 - self.tests_done = list() - self.check_start_time = 0 - self.check_stop_time = 0 - self.rsp_driver_down = False - self.tbb_driver_down = False - - self.station_error = 0 - self.rspdriver_version = "ok" - self.rspctl_version = "ok" - self.tbbdriver_version = "ok" - self.tbbctl_version = "ok" - - self.test_end_time = -1 - - self.spu = list() - for i in range(self.nr_spu): - self.spu.append(self.cSPU(i)) - - self.rsp = list() - for i in range(nRSP): - self.rsp.append(self.cRSP(i)) - - self.tbb = list() - for i in range(nTBB): - self.tbb.append(self.cTBB(i)) - - self.rcu_state = list() - for i in range(self.nr_rcu): - self.rcu_state.append(0) - - self.lbl = deepcopy(self.cLBA_DB(label='LBL', nr_antennas=nLBL, nr_offset=48)) - self.lbh = deepcopy(self.cLBA_DB(label='LBH', nr_antennas=nLBH, nr_offset=0)) - self.hba = deepcopy(self.cHBA_DB(nr_tiles=nHBA, split=self.hba_split)) - - def setTestEndTime(self, end_time): - if end_time > time.time(): - self.test_end_time = end_time - else: - logger.warn("end time in past") - return - - # returns True if before end time - def checkEndTime(self, duration=0.0): - if self.test_end_time == -1: - return (True) - if (time.time() + duration) < self.test_end_time: - return (True) - else: - return (False) - - # add only ones - def addTestDone(self, name): - if name not in self.tests_done: - self.tests_done.append(name) - - # check if already done - def isTestDone(self, name): - if name in self.tests_done: - return (False) - return (True) - - # test - def test(self, logdir): - if self.rspdriver_version != "ok" or self.rspctl_version != "ok": - self.station_error = 1 - - if self.tbbdriver_version != "ok" or self.tbbctl_version != "ok": - self.station_error = 1 - - for _spu in self.spu: - ok = _spu.test() - if not ok: - self.station_error = 1 - - for _rsp in self.rsp: - ok = _rsp.test() - if not ok: - self.station_error = 1 - - for _tbb in self.tbb: - ok = _tbb.test() - if not ok: - self.station_error = 1 - - # test rcu's first - for _rcu in range(self.nr_rcu): - error_count = 0 - - ant_nr = _rcu / 2 - pol_nr = _rcu % 2 # 0=X, 1=Y - - if pol_nr == 0: - if self.nr_lbl > 0 and self.lbl.ant[ant_nr].x.error: error_count += 1 - if self.lbh.ant[ant_nr].x.error: error_count += 1 - if self.hba.tile[ant_nr].x.rcu_error: error_count += 1 - else: - if self.nr_lbl > 0 and self.lbl.ant[ant_nr].y.error: error_count += 1 - if self.lbh.ant[ant_nr].y.error: error_count += 1 - if self.hba.tile[ant_nr].y.rcu_error: error_count += 1 - - if error_count >= 2: - self.rcu_state[_rcu] = 1 - - self.station_error = max(self.station_error, self.lbl.test(), self.lbh.test(), self.hba.test()) - - self.makeLogFile(logdir) - return (self.station_error) - - - # make standard log file - def makeLogFile(self, logdir): - #print logdir - date = getShortDateStr(self.check_start_time) - log = cTestLogger(logdir) - - log.addLine("%s,NFO,---,VERSIONS,%s" %(date, self.script_versions)) - - log.addLine("%s,NFO,---,STATION,NAME=%s" %(date, getHostName())) - - log.addLine("%s,NFO,---,RUNTIME,START=%s,STOP=%s" %(date, getDateTimeStr(self.check_start_time), getDateTimeStr(self.check_stop_time))) - - info = "" - bad = "" - for ant in self.lbl.ant: - if ant.on_bad_list == 1: - bad += "%d " %(ant.nr_pvss) - if len(bad) > 0: - info += "LBL=%s," %(bad[:-1]) - - bad = "" - for ant in self.lbh.ant: - if ant.on_bad_list == 1: - bad += "%d " %(ant.nr_pvss) - if len(bad) > 0: - info += "LBH=%s," %(bad[:-1]) - - bad = "" - for tile in self.hba.tile: - if tile.on_bad_list == 1: - bad += "%d " %(tile.nr) - if len(bad) > 0: - info += "HBA=%s," %(bad[:-1]) - if len(info) > 0: - log.addLine("%s,NFO,---,BADLIST,%s" %(date, info[:-1])) - - if self.rsp_driver_down: - log.addLine("%s,NFO,---,DRIVER,RSPDRIVER=DOWN" %(date)) - - if self.tbb_driver_down: - log.addLine("%s,NFO,---,DRIVER,TBBDRIVER=DOWN" %(date)) - - if len(self.board_errors): - boardstr = '' - for board in self.board_errors: - boardstr += "RSP-%d=DOWN," %(board) - log.addLine("%s,NFO,---,BOARD,%s" %(date, boardstr[:-1])) - - log.addLine("%s,NFO,---,CHECKS,%s" %(date, string.join(self.tests_done, ','))) - - log.addLine("%s,NFO,---,STATISTICS,BAD_LBL=%d,BAD_LBH=%d,BAD_HBA=%d,BAD_HBA0=%d,BAD_HBA1=%d" %\ - (date, self.lbl.nr_bad_antennas, self.lbh.nr_bad_antennas, self.hba.nr_bad_tiles, self.hba.nr_bad_tiles_0, self.hba.nr_bad_tiles_1)) - - - if self.rspdriver_version != "ok" or self.rspctl_version != "ok": - log.addLine("%s,RSP,---,VERSION,RSPDRIVER=%s,RSPCTL=%s" %\ - (date, self.rspdriver_version, self.rspctl_version)) - - for spu in self.spu: - spu.test() - if not spu.voltage_ok: - valstr = '' - if not spu.rcu_ok: valstr += ",RCU-5.0V=%3.1f" %(spu.rcu_5_0V) - if not spu.lba_ok: valstr += ",LBA-8.0V=%3.1f" %(spu.lba_8_0V) - if not spu.hba_ok: valstr += ",HBA-48V=%3.1f" %(spu.hba_48V) - if not spu.spu_ok: valstr += ",SPU-3.3V=%3.1f" %(spu.spu_3_3V) - if len(valstr): - log.addLine("%s,SPU,%03d,VOLTAGE%s" %(date, spu.nr, valstr)) - - if not spu.temp_ok: - log.addLine("%s,SPU,%03d,TEMPERATURE,PCB=%2.0f" %\ - (date, spu.nr, spu.temp)) - - for rsp in self.rsp: - rsp.test() - if not rsp.version_ok: - log.addLine("%s,RSP,%03d,VERSION,BP=%s,AP=%s" %\ - (date, rsp.nr, rsp.bp_version, rsp.ap_version)) - if not rsp.voltage_ok: - log.addLine("%s,RSP,%03d,VOLTAGE,1.2V=%3.2f,2.5V=%3.2f,3.3V=%3.2f" %\ - (date,rsp.nr, rsp.voltage1_2, rsp.voltage2_5, rsp.voltage3_3)) - if not rsp.temp_ok: - log.addLine("%s,RSP,%03d,TEMPERATURE,PCB=%2.0f,BP=%2.0f,AP0=%2.0f,AP1=%2.0f,AP2=%2.0f,AP3=%2.0f" %\ - (date,rsp.nr, rsp.pcb_temp, rsp.bp_temp, rsp.ap0_temp, rsp.ap1_temp, rsp.ap2_temp, rsp.ap3_temp)) - - if self.tbbdriver_version != "ok" or self.tbbctl_version != "ok": - log.addLine("%s,TBB,---,VERSION,TBBDRIVER=%s,TBBCTL=%s" %\ - (date, self.tbbdriver_version, self.tbbctl_version)) - - for tbb in self.tbb: - tbb.test() - if not tbb.version_ok: - log.addLine("%s,TBB,%03d,VERSION,TP=%s,MP=%s" %\ - (date, tbb.nr, tbb.tp_version, tbb.mp_version)) - if not tbb.memory_ok: - log.addLine("%s,TBB,%03d,MEMORY" %(date, tbb.nr)) - - for rcu in range(self.nr_rcu): - if self.rcu_state[rcu]: - log.addLine("%s,RCU,%03d,BROKEN" % (date, rcu)) - - # lbl/lbh - for lba in (self.lbl, self.lbh): - - if lba.signal_check_done: - if lba.test_signal_x == 0 or lba.test_signal_y == 0: - log.addLine("%s,%s,---,NOSIGNAL" %(date, lba.label)) - - elif lba.avg_2_low: - log.addLine("%s,%s,---,TOOLOW,AVGX=%3.1f,AVGY=%3.1f" %(date, lba.label, lba.avg_x, lba.avg_y)) - - else: - if lba.error: - log.addLine("%s,%s,---,TESTSIGNAL,SUBBANDX=%d,SIGNALX=%3.1f,SUBBANDY=%d,SIGNALY=%3.1f" %\ - (date, lba.label, lba.test_subband_x, lba.test_signal_x, lba.test_subband_y, lba.test_signal_y)) - - - if lba.noise_check_done or lba.oscillation_check_done or lba.spurious_check_done or lba.signal_check_done or\ - lba.short_check_done or lba.flat_check_done or lba.down_check_done: - for ant in lba.ant: - if ant.down: - log.addLine("%s,%s,%03d,DOWN,X=%3.1f,Y=%3.1f,Xoff=%d,Yoff=%d" %\ - (date, lba.label, ant.nr_pvss, ant.x.test_signal, ant.y.test_signal, ant.x.offset, ant.y.offset)) - else: - if lba.signal_check_done: - valstr = '' - if ant.x.too_low or ant.x.too_high: valstr += ",X=%3.1f" %(ant.x.test_signal) - if ant.y.too_low or ant.y.too_high: valstr += ",Y=%3.1f" %(ant.y.test_signal) - if len(valstr): - log.addLine("%s,%s,%03d,RF_FAIL%s" %(date, lba.label, ant.nr_pvss, valstr)) - - if lba.flat_check_done: - valstr = '' - if ant.x.flat: valstr += ",Xmean=%3.1f" %(ant.x.flat_val) - if ant.y.flat: valstr += ",Ymean=%3.1f" %(ant.y.flat_val) - if len(valstr): - log.addLine("%s,%s,%03d,FLAT%s" %(date, lba.label, ant.nr_pvss, valstr)) - - if lba.short_check_done: - valstr = '' - if ant.x.short: valstr += ",Xmean=%3.1f" %(ant.x.short_val) - if ant.y.short: valstr += ",Ymean=%3.1f" %(ant.y.short_val) - if len(valstr): - log.addLine("%s,%s,%03d,SHORT%s" %(date, lba.label, ant.nr_pvss, valstr)) - - if lba.oscillation_check_done: - valstr = '' - if ant.x.osc: valstr += ',X=1' - if ant.y.osc: valstr += ',Y=1' - if len(valstr): - log.addLine("%s,%s,%03d,OSCILLATION%s" %(date, lba.label, ant.nr_pvss, valstr)) - - if lba.spurious_check_done: - valstr = '' - if ant.x.spurious: valstr += ',X=1' - if ant.y.spurious: valstr += ',Y=1' - if len(valstr): - log.addLine("%s,%s,%03d,SPURIOUS%s" %(date, lba.label, ant.nr_pvss, valstr)) - - if lba.noise_check_done: - noise = False - valstr = '' - if not ant.x.flat and ant.x.low_noise: - proc = (100.0 / ant.x.low_seconds) * ant.x.low_bad_seconds - valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' %(proc, ant.x.low_val, ant.x.low_diff, ant.x.low_ref) - if not ant.y.flat and ant.y.low_noise: - proc = (100.0 / ant.y.low_seconds) * ant.y.low_bad_seconds - valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' %(proc, ant.y.low_val, ant.y.low_diff, ant.y.low_ref) - if len(valstr): - log.addLine("%s,%s,%03d,LOW_NOISE%s" %(date, lba.label, ant.nr_pvss, valstr)) - noise = True - - valstr = '' - if ant.x.high_noise: - proc = (100.0 / ant.x.high_seconds) * ant.x.high_bad_seconds - valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' %(proc, ant.x.high_val, ant.x.high_diff, ant.x.high_ref) - if ant.y.high_noise: - proc = (100.0 / ant.y.high_seconds) * ant.y.high_bad_seconds - valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' %(proc, ant.y.high_val, ant.y.high_diff, ant.y.high_ref) - if len(valstr): - log.addLine("%s,%s,%03d,HIGH_NOISE%s" %(date, lba.label, ant.nr_pvss, valstr)) - noise = True - - valstr = '' - if not noise and ant.x.jitter: - proc = (100.0 / ant.x.jitter_seconds) * ant.x.jitter_bad_seconds - valstr += ',Xproc=%5.3f,Xdiff=%5.3f,Xref=%3.1f' %(proc, ant.x.jitter_val, ant.x.jitter_ref) - if not noise and ant.y.jitter: - proc = (100.0 / ant.y.jitter_seconds) * ant.y.jitter_bad_seconds - valstr += ',Xproc=%5.3f,Ydiff=%5.3f,Yref=%3.1f' %(proc, ant.y.jitter_val, ant.y.jitter_ref) - if len(valstr): - log.addLine("%s,%s,%03d,JITTER%s" %(date, lba.label, ant.nr_pvss, valstr)) - lba = None - # end lbl/lbh - - - # hba - if self.hba.signal_check_done: - valstr = '' - if self.hba.ref_signal_x[0] == 0 and self.hba.ref_signal_x[1] == 0: - valstr += ",X" - if self.hba.ref_signal_y[0] == 0 and self.hba.ref_signal_y[1] == 0: - valstr += ",Y" - if len(valstr): - log.addLine("%s,HBA,---,NOSIGNAL%s" %(date, valstr)) - - for tile in self.hba.tile: - if tile.x.error or tile.y.error: - # check for broken summators - if self.hba.modem_check_done: - valstr = '' - if tile.c_summator_error: - log.addLine("%s,HBA,%03d,C_SUMMATOR" %(date, tile.nr)) - else: - for elem in tile.element: - if elem.no_modem: - valstr += ",E%02d=??" %(elem.nr) - - elif elem.modem_error: - valstr += ",E%02d=error" %(elem.nr) - if len(valstr): - log.addLine("%s,HBA,%03d,MODEM%s" %(date, tile.nr, valstr)) - - if self.hba.noise_check_done: - valstr = '' - noise = False - - if tile.x.low_noise: - proc = (100.0 / tile.x.low_seconds) * tile.x.low_bad_seconds - valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' %(proc, tile.x.low_val, tile.x.low_diff, tile.x.low_ref) - if tile.y.low_noise: - proc = (100.0 / tile.y.low_seconds) * tile.y.low_bad_seconds - valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' %(proc, tile.y.low_val, tile.y.low_diff, tile.y.low_ref) - if len(valstr): - log.addLine("%s,HBA,%03d,LOW_NOISE%s" %(date, tile.nr, valstr)) - noise = True - - valstr = '' - if tile.x.high_noise: - proc = (100.0 / tile.x.high_seconds) * tile.x.high_bad_seconds - valstr += ',Xproc=%5.3f,Xval=%3.1f,Xdiff=%5.3f,Xref=%3.1f' %(proc, tile.x.high_val, tile.x.high_diff, tile.x.high_ref) - if tile.y.high_noise: - proc = (100.0 / tile.y.high_seconds) * tile.y.high_bad_seconds - valstr += ',Yproc=%5.3f,Yval=%3.1f,Ydiff=%5.3f,Yref=%3.1f' %(proc, tile.y.high_val, tile.y.high_diff, tile.y.high_ref) - if len(valstr): - log.addLine("%s,HBA,%03d,HIGH_NOISE%s" %(date, tile.nr, valstr)) - noise = True - - valstr = '' - if (not noise) and tile.x.jitter: - proc = (100.0 / tile.x.jitter_seconds) * tile.x.jitter_bad_seconds - valstr += ',Xproc=%5.3f,Xdiff=%5.3f,Xref=%3.1f' %(proc, tile.x.jitter_val, tile.x.jitter_ref) - if (not noise) and tile.y.jitter: - proc = (100.0 / tile.y.jitter_seconds) * tile.y.jitter_bad_seconds - valstr += ',Yproc=%5.3f,Ydiff=%5.3f,Yref=%3.1f' %(proc, tile.y.jitter_val, tile.y.jitter_ref) - if len(valstr): - log.addLine("%s,HBA,%03d,JITTER%s" %(date, tile.nr, valstr)) - - if self.hba.oscillation_check_done: - valstr = '' - if tile.x.osc: valstr += ',X=1' - if tile.y.osc: valstr += ',Y=1' - if len(valstr): - log.addLine("%s,HBA,%03d,OSCILLATION%s" %(date, tile.nr, valstr)) - - if tile.p_summator_error: - log.addLine("%s,HBA,%03d,P_SUMMATOR" %(date, tile.nr)) - - - - if self.hba.summatornoise_check_done: - valstr = '' - if tile.x.summator_noise: valstr += ',X=1' - if tile.y.summator_noise: valstr += ',Y=1' - if len(valstr): - log.addLine("%s,HBA,%03d,SUMMATOR_NOISE%s" %(date, tile.nr, valstr)) - - if self.hba.spurious_check_done: - valstr = '' - if tile.x.spurious: valstr += ',X=1' - if tile.y.spurious: valstr += ',Y=1' - if len(valstr): - log.addLine("%s,HBA,%03d,SPURIOUS%s" %(date, tile.nr, valstr)) - - if self.hba.signal_check_done: - valstr = '' - if tile.x.too_low or tile.x.too_high: - valstr += ",X=%3.1f %d %3.1f %3.1f %d %3.1f" %\ - (tile.x.test_signal[0], self.hba.test_subband_x[0], self.hba.ref_signal_x[0],\ - tile.x.test_signal[1], self.hba.test_subband_x[1], self.hba.ref_signal_x[1]) - if tile.y.too_low or tile.y.too_high: - valstr += ",Y=%3.1f %d %3.1f %3.1f %d %3.1f" %\ - (tile.y.test_signal[0], self.hba.test_subband_y[0], self.hba.ref_signal_y[0],\ - tile.y.test_signal[1], self.hba.test_subband_y[1], self.hba.ref_signal_y[1]) - if len(valstr): - log.addLine("%s,HBA,%03d,RF_FAIL%s" %(date, tile.nr, valstr)) - - valstr = '' - if self.hba.element_check_done: - for elem in tile.element: - #if tile.x.rcu_off or tile.y.rcu_off: - # continue - if self.hba.modem_check_done and (elem.no_modem or elem.modem_error): - if not tile.c_summator_error: - if elem.no_modem: - valstr += ",M%d=??" %(elem.nr) - - elif elem.modem_error: - valstr += ",M%d=error" %(elem.nr) - else: - if elem.x.osc or elem.y.osc: - if elem.x.osc: - valstr += ",OX%d=1" %(elem.nr) - if elem.y.osc: - valstr += ",OY%d=1" %(elem.nr) - - elif elem.x.spurious or elem.y.spurious: - if elem.x.spurious: - valstr += ",SPX%d=1" %(elem.nr) - if elem.y.spurious: - valstr += ",SPY%d=1" %(elem.nr) - - elif elem.x.low_noise or elem.x.high_noise or elem.y.low_noise or elem.y.high_noise or elem.x.jitter or elem.y.jitter: - if elem.x.low_noise: - valstr += ",LNX%d=%3.1f %5.3f" %(elem.nr, elem.x.low_val, elem.x.low_diff) - - if elem.x.high_noise: - valstr += ",HNX%d=%3.1f %5.3f" %(elem.nr, elem.x.high_val, elem.x.high_diff) - - if (not elem.x.low_noise) and (not elem.x.high_noise) and (elem.x.jitter > 0.0): - valstr += ",JX%d=%5.3f" %(elem.nr, elem.x.jitter) - - if elem.y.low_noise: - valstr += ",LNY%d=%3.1f %5.3f" %(elem.nr, elem.y.low_val, elem.y.low_diff) - - if elem.y.high_noise: - valstr += ",HNY%d=%3.1f %5.3f" %(elem.nr, elem.y.high_val, elem.y.high_diff) - - if (not elem.y.low_noise) and (not elem.y.high_noise) and (elem.y.jitter > 0.0): - valstr += ",JY%d=%5.3f" %(elem.nr, elem.y.jitter) - else: - if elem.x.ref_signal[0] == 0 and elem.x.ref_signal[1] == 0: - log.addLine("%s,HBA,%03d,NOSIGNAL,E%02dX" %(date, tile.nr, elem.nr)) - else: - if elem.x.error: - valstr += ",X%d=%3.1f %d %3.1f %3.1f %d %3.1f" %\ - (elem.nr,\ - elem.x.test_signal[0], elem.x.test_subband[0], elem.x.ref_signal[0],\ - elem.x.test_signal[1], elem.x.test_subband[1], elem.x.ref_signal[1]) - - if elem.y.ref_signal[0] == 0 and elem.y.ref_signal[1] == 0: - log.addLine("%s,HBA,%03d,NOSIGNAL,E%02dY" %(date, tile.nr, elem.nr)) - else: - if elem.y.error: - valstr += ",Y%d=%3.1f %d %3.1f %3.1f %d %3.1f" %\ - (elem.nr,\ - elem.y.test_signal[0], elem.y.test_subband[0], elem.y.ref_signal[0],\ - elem.y.test_signal[1], elem.y.test_subband[1], elem.y.ref_signal[1]) - - if len(valstr): - log.addLine("%s,HBA,%03d,E_FAIL%s" %(date, tile.nr, valstr)) - # end HBA - return - -#======================================================================================================================= -# database from here - class cSPU: - def __init__(self, nr): - self.nr = nr - self.rcu_5_0V = 0.0 - self.lba_8_0V = 0.0 - self.hba_48V = 0.0 - self.spu_3_3V = 0.0 - self.rcu_ok = 1 - self.lba_ok = 1 - self.hba_ok = 1 - self.spu_ok = 1 - self.voltage_ok = 1 - self.temp = 0.0 - self.temp_ok = 1 - - def test(self): - self.voltage_ok = 0 - if self.rcu_ok and self.lba_ok and self.hba_ok and self.spu_ok: - self.voltage_ok = 1 - return(self.voltage_ok) - - - class cRSP: - def __init__(self, nr): - self.nr = nr - - self.test_done = 0 - self.board_ok = 1 - self.ap_version = 'ok' - self.bp_version = 'ok' - self.version_ok = 1 - self.voltage1_2 = 0.0 - self.voltage2_5 = 0.0 - self.voltage3_3 = 0.0 - self.voltage_ok = 1 - self.pcb_temp = 0.0 - self.bp_temp = 0.0 - self.ap0_temp = 0.0 - self.ap1_temp = 0.0 - self.ap2_temp = 0.0 - self.ap3_temp = 0.0 - self.temp_ok = 1 - - def test(self): - if self.ap_version != 'ok' or self.bp_version != 'ok': - self.version_ok = 0 - return (self.version_ok and self.voltage_ok and self.temp_ok) - - # used by LBA and HBA antenna class - class cPolarity: - def __init__(self, rcu=None): - - self.rcu = rcu - self.rcu_off = 0 # 0 = RCU on, 1 = RCU off - self.rcu_error = 0 - - # status variables 0|1 - self.error = 0 # - self.too_low = 0 # - self.too_high = 0 # - self.low_noise = 0 # - self.high_noise = 0 # - self.jitter = 0 # - self.osc = 0 # - self.no_signal = 0 # signal below 2dB - self.summator_noise = 0 # - self.spurious = 0 # - self.flat = 0 # - self.short = 0 # - - # test result of signal test, - # only for HBA element test, firt value ctrl=129 second value ctrl=253 - self.test_subband = [0, 0] - self.ref_signal = [-1, -1] - self.test_signal = [0.0, 0.0] - self.offset = 0 - - # measured values filled on error - # proc : bad time in meausured time 0..100% - # val : max or min meausured value - self.low_seconds = 0 - self.low_bad_seconds = 0 - self.low_val = 100.0 # - self.low_diff = 0.0 - self.low_ref = 0.0 # - - self.high_seconds = 0 - self.high_bad_seconds = 0 - self.high_val = 0.0 # - self.high_diff = 0.0 - self.high_ref = 0.0 # - - self.jitter_seconds = 0 - self.jitter_bad_seconds = 0 - self.jitter_val = 0.0 - self.jitter_ref = 0.0 - - self.flat_val = 0.0 - self.short_val = 0.0 - - class cLBA_DB: - def __init__(self, label, nr_antennas, nr_offset=0): - self.rsp_driver_down = False - self.noise_check_done = 0 - self.signal_check_done = 0 - self.short_check_done = 0 - self.flat_check_done = 0 - self.down_check_done = 0 - self.spurious_check_done = 0 - self.oscillation_check_done = 0 - - self.noise_low_deviation = 0.0 - self.noise_high_deviation = 0.0 - self.noise_max_fluctuation = 0.0 - - self.rf_low_deviation = 0.0 - self.rf_high_deviation = 0.0 - self.rf_subband = 0 - - self.check_time_noise = 0 - self.nr_antennas = nr_antennas - self.nr_offset = nr_offset - self.label = label - self.error = 0 - self.avg_2_low = 0 - self.avg_x = 0 - self.avg_y = 0 - self.test_subband_x = 0 - self.test_subband_y = 0 - self.test_signal_x = 0 - self.test_signal_y = 0 - self.nr_bad_antennas = -1 - self.ant = list() - for i in range(self.nr_antennas): - self.ant.append(self.cAntenna(i, self.nr_offset)) - return - - def test(self): - if self.rsp_driver_down: - return (self.error) - if self.noise_check_done or self.signal_check_done or self.short_check_done or self.flat_check_done or\ - self.down_check_done or self.signal_check_done or self.spurious_check_done or self.oscillation_check_done: - self.nr_bad_antennas = 0 - - for ant in self.ant: - ant.test() - ant_error = max(ant.x.error, ant.y.error) - self.error = max(self.error, ant_error) - if ant_error: - self.nr_bad_antennas += 1 - return (self.error) - - # return select string for rspctl command - def selectList(self): - select = list() - for ant in self.ant: - if ant.on_bad_list == 0: - select.append(ant.x.rcu) - select.append(ant.y.rcu) - return (select) - - def resetRcuState(self): - for ant in self.ant: - ant.x.rcu_off = 0 - ant.y.rcu_off = 0 - - class cAntenna: - def __init__(self, nr, nr_offset): - self.nr = nr - self.nr_pvss = self.nr + nr_offset - self.on_bad_list = 0 - self.x = cDB.cPolarity(rcu=(self.nr * 2)) - self.y = cDB.cPolarity(rcu=((self.nr * 2) + 1)) - - self.down = 0 - return - - def test(self): - self.x.error = max(self.x.too_low, self.x.too_high, self.x.osc, self.x.high_noise, self.x.low_noise, - self.x.jitter, self.x.spurious, self.down, self.x.flat, self.x.short) - self.y.error = max(self.y.too_low, self.y.too_high, self.y.osc, self.y.high_noise, self.y.low_noise, - self.y.jitter, self.y.spurious, self.down, self.y.flat, self.y.short) - return - - class cHBA_DB: - def __init__(self, nr_tiles, split): - self.rsp_driver_down = False - self.modem_check_done = 0 - self.noise_check_done = 0 - self.signal_check_done = 0 - self.spurious_check_done = 0 - self.oscillation_check_done = 0 - self.summatornoise_check_done = 0 - self.element_check_done = 0 - - self.hba_split = split - self.check_time_noise = 0 - self.check_time_noise_elements = 0 - self.nr_tiles = nr_tiles - self.error = 0 - self.avg_2_low = 0 - # only used for tile RF test - # first value ctrl=129 second value ctrl=253 - self.test_subband_x = [0, 0] - self.test_subband_y = [0, 0] - self.ref_signal_x = [0.0, 0.0] - self.ref_signal_y = [0.0, 0.0] - self.tile = list() - self.nr_bad_tiles = -1 - self.nr_bad_tiles_0 = -1 - self.nr_bad_tiles_1 = -1 - for i in range(self.nr_tiles): - self.tile.append(self.cTile(i)) - return - - def test(self): - if self.rsp_driver_down: - return (self.error) - if self.modem_check_done or self.noise_check_done or self.signal_check_done or self.spurious_check_done \ - or self.oscillation_check_done or self.summatornoise_check_done or self.element_check_done : - if self.hba_split == 1: - self.nr_bad_tiles_0 = 0 - self.nr_bad_tiles_1 = 0 - else: - self.nr_bad_tiles = 0 - - for tile in self.tile: - tile.test() - tile_error = max(tile.x.error, tile.y.error) - self.error = max(self.error, tile_error) - - if tile_error: - if self.hba_split == 1: - if tile.nr < 24: - self.nr_bad_tiles_0 += 1 - else: - self.nr_bad_tiles_1 += 1 - else: - self.nr_bad_tiles += 1 - return (self.error) - - # return select string for rspctl command - def selectList(self): - select = list() - for tile in self.tile: - if tile.on_bad_list == 0: - select.append(tile.x.rcu) - select.append(tile.y.rcu) - return (select) - - def resetRcuState(self): - for tile in self.tile: - tile.x.rcu_off = 0 - tile.y.rcu_off = 0 - - class cTile: - def __init__(self, nr): - self.nr = nr - self.on_bad_list = 0 - self.x = cDB.cPolarity(rcu=(nr*2)) - self.y = cDB.cPolarity(rcu=(nr*2+1)) - - self.noise_low_deviation = 0.0 - self.noise_high_deviation = 0.0 - self.noise_max_fluctuation = 0.0 - - self.rf_low_deviation = 0.0 - self.rf_high_deviation = 0.0 - self.rf_subband = 0 - - self.no_power = 0 # signal around 60dB - self.p_summator_error = 0 - self.c_summator_error = 0 - self.nr_elements = 16 - self.element = list() - for i in range(1,self.nr_elements+1,1): - self.element.append(self.cElement(i)) - return - - def test(self): - no_modem_cnt = 0 - modem_err_cnt = 0 - no_power_cnt = 0 - x_no_signal_cnt = 0 - y_no_signal_cnt = 0 - for elem in self.element: - elem.test() - if elem.x.no_signal: - x_no_signal_cnt += 1 - if elem.y.no_signal: - y_no_signal_cnt += 1 - if elem.no_power: - no_power_cnt += 1 - if elem.no_modem: - no_modem_cnt += 1 - if elem.modem_error: - modem_err_cnt += 1 - - self.x.error = max(self.x.error, elem.x.error) - self.y.error = max(self.y.error, elem.y.error) - - if (no_modem_cnt >= 8) or (modem_err_cnt >= 8): - self.c_summator_error = 1 - if no_power_cnt >= 15: - self.p_summator_error = 1 - if x_no_signal_cnt == 16: - self.x.rcu_error = 1 - if y_no_signal_cnt == 16: - self.y.rcu_error = 1 - - self.x.error = max(self.x.error, self.x.too_low, self.x.too_high, self.x.low_noise, self.x.no_signal, self.x.high_noise, self.x.jitter, self.x.osc, - self.x.summator_noise, self.x.spurious, self.p_summator_error, self.c_summator_error) - - self.y.error = max(self.y.error, self.y.too_low, self.y.too_high, self.y.low_noise, self.y.no_signal, self.y.high_noise, self.y.jitter, self.y.osc, - self.y.summator_noise, self.y.spurious, self.p_summator_error, self.c_summator_error) - return - - - class cElement: - def __init__(self, nr): - self.nr = nr - self.x = cDB.cPolarity() - self.y = cDB.cPolarity() - - self.noise_low_deviation = 0.0 - self.noise_high_deviation = 0.0 - self.noise_max_fluctuation = 0.0 - - self.rf_low_deviation = 0.0 - self.rf_high_deviation = 0.0 - self.rf_subband = 0 - - self.no_power = 0 # signal around 60dB - self.no_modem = 0 # modem reponse = ?? - self.modem_error = 0 # wrong response from modem - - return - - def test(self): - modem_err = 0 - if self.no_modem or self.modem_error: - modem_err = 1 - - self.x.error = max(self.x.too_low, self.x.too_high, self.x.low_noise, self.x.high_noise, self.x.no_signal, - self.x.jitter, self.no_power, self.x.spurious, self.x.osc, modem_err) - - self.y.error = max(self.y.too_low, self.y.too_high, self.y.low_noise, self.y.high_noise, self.y.no_signal, - self.y.jitter, self.no_power, self.y.spurious, self.y.osc, modem_err) - return - - - class cTDS: - def __init__(self): - self.test_done = 0 - self.ok = 1 - - class cTBB: - def __init__(self, nr): - self.nr = nr - self.test_done = 0 - self.tp_version = 'ok' - self.mp_version = 'ok' - self.memory_size = 0 - self.version_ok = 1 - self.memory_ok = 1 - - def test(self): - if self.tp_version != 'ok' or self.mp_version != 'ok': - self.version_ok = 0 - if self.memory_size != 0: - self.memory_ok = 0 - - return - - diff --git a/LCU/checkhardware/lib/test_lib.py b/LCU/checkhardware/lib/test_lib.py deleted file mode 100644 index 7a953b02802adbdcc6b3744e78257bd22c612742..0000000000000000000000000000000000000000 --- a/LCU/checkhardware/lib/test_lib.py +++ /dev/null @@ -1,1623 +0,0 @@ -#!/usr/bin/python -# test lib - -from general_lib import * -from lofar_lib import * -from search_lib import * -from data_lib import * -import os -import numpy as np -import logging - -test_version = '0815' - -logger = None -def init_test_lib(): - global logger - logger = logging.getLogger() - logger.debug("init logger test_lib") - - -#HBASubband = dict( DE601C=155, DE602C=155, DE603C=284, DE604C=474, DE605C=479, FR606C=155, SE607C=287, UK608C=155 ) -#DefaultLBASubband = 301 -#DefaultHBASubband = 155 - -class cSPU: - def __init__(self, db): - self.db = db - self.board_info_str = [] - self.board_info_val = [-1, 0.0, 0.0, 0.0, 0.0, 0] - - def extract_board_info(self, line): - li = line.split("|") - if not li[0].strip().isdigit(): - return False - - self.board_info_str = [i.strip() for i in li] - - if li[0].strip().isdigit(): - self.board_info_val[0] = int(li[0].strip()) - else: - self.board_info_val[0] = -1 - - for i in xrange(1, 5, 1): - if li[i].strip().replace('.','').isdigit(): - self.board_info_val[i] = float(li[i].strip()) - else: - self.board_info_val[i] = 0.0 - - if li[5].strip().isdigit(): - self.board_info_val[5] = int(li[5].strip()) - else: - self.board_info_val[5] = 0 - return True - - - def checkStatus(self): - """ - check PSU if boards idle and fully loaded - """ - # in future get all settings from configuration file - max_3_3 = 3.4 - min_3_3 = 3.1 - max_drop_3_3 = 0.3 - - max_5_0 = 5.0 - min_5_0 = 4.5 - max_drop_5_0 = 0.3 - - max_8_0 = 8.0 - min_8_0 = 7.4 - max_drop_8_0 = 0.3 - - max_48 = 48.0 - min_48 = 43.0 - max_drop_48 = 2.0 - - logger.info("=== SPU status check ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - noload = [] - fullload_3 = [] - fullload_5 = [] - logger.debug("check spu no load") - answer = rspctl('--spustatus') - - # check if Driver is available - if answer.find('No Response') > 0: - logger.warn("No RSPDriver") - self.db.rspdriver_version = 0 - else: - infolines = answer.splitlines() - for line in infolines: - if self.extract_board_info(line): - bi = self.board_info_val - noload.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) - self.db.spu[bi[0]].temp = bi[5] - bi = self.board_info_str - logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( - bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) - - # turn on all hbas - logger.debug("check spu full load mode 3") - rsp_rcu_mode(3, self.db.lbh.selectList()) - answer = rspctl('--spustatus') - infolines = answer.splitlines() - for line in infolines: - if self.extract_board_info(line): - bi = self.board_info_val - fullload_3.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) - bi = self.board_info_str - logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( - bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) - - # turn on all hbas - logger.debug("check spu full load mode 5") - rsp_rcu_mode(5, self.db.hba.selectList()) - answer = rspctl('--spustatus') - infolines = answer.splitlines() - for line in infolines: - if self.extract_board_info(line): - bi = self.board_info_val - fullload_5.append([bi[0], bi[1], bi[2], bi[3], bi[4]]) - bi = self.board_info_str - logger.debug("Subrack %s voltages: rcu=%s lba=%s hba=%s spu=%s temp: %s" % ( - bi[0], bi[1], bi[2], bi[3], bi[4], bi[5])) - - for sr in range(self.db.nr_spu): - # calculate mean of noload, fullload_3, fullload_5 - self.db.spu[sr].rcu_5_0V = (noload[sr][1] + fullload_3[sr][1] + fullload_5[sr][1]) / 3.0 - self.db.spu[sr].lba_8_0V = fullload_3[sr][2] - self.db.spu[sr].hba_48V = fullload_5[sr][3] - self.db.spu[sr].spu_3_3V = (noload[sr][4] + fullload_3[sr][4] + fullload_5[sr][4]) / 3.0 - - if (self.db.spu[sr].temp > 35.0): - self.db.spu[sr].temp_ok = 0 - - if (not (min_5_0 <= noload[sr][1] <= max_5_0) - or not (min_5_0 < fullload_3[sr][1] <= max_5_0) - or not (min_5_0 <= fullload_5[sr][1] <= max_5_0) - or (noload[sr][1] - fullload_3[sr][1]) > max_drop_5_0): - self.db.spu[sr].rcu_ok = 0 - logger.info("SPU voltage 5.0V out of range") - - if (not (min_8_0 <= noload[sr][2] <= max_8_0) - or not (min_8_0 <= fullload_3[sr][2] <= max_8_0) - or (noload[sr][2] - fullload_3[sr][2]) > max_drop_8_0): - self.db.spu[sr].lba_ok = 0 - logger.info("SPU voltage 8.0V out of range") - - if (not (min_48 <= noload[sr][3] <= max_48) - or not (min_48 <= fullload_5[sr][3] <= max_48) - or (noload[sr][3] - fullload_5[sr][3]) > max_drop_48): - self.db.spu[sr].hba_ok = 0 - logger.info("SPU voltage 48V out of range") - - if (not (min_3_3 <= noload[sr][4] <= max_3_3) - or not (min_3_3 <= fullload_3[sr][4] <= max_3_3) - or not (min_3_3 <= fullload_5[sr][4] <= max_3_3) - or (noload[sr][4] - fullload_5[sr][4]) > max_drop_3_3): - self.db.spu[sr].spu_ok = 0 - logger.info("SPU voltage 3.3V out of range") - - logger.info("=== Done SPU check ===") - self.db.addTestDone('SPU') - return - -# class for checking TBB boards using tbbctl -class cTBB: - global logger - def __init__(self, db): - self.db = db - self.nr = self.db.nr_tbb - self.driverstate = True - #tbbctl('--free') - - # check software versions of driver, tbbctl and TP/MP firmware - def checkVersions(self, driverV, tbbctlV, tpV, mpV ): - logger.info("=== TBB Version check ===") - answer = tbbctl('--version') - - # check if Driver is available - if answer.find('TBBDriver is NOT responding') > 0: - logger.warn("No TBBDriver") - self.driverstate = False - self.db.tbbdriver_version = 0 - else: - infolines = answer.splitlines() - info = infolines[4:6] + infolines[9:-1] - #print info - if info[0].split()[-1] != driverV: - logger.warn("Not right Driver version") - self.db.tbbdriver_version = info[0].split()[-1] - - if info[1].split()[-1] != tbbctlV: - logger.warn("Not right tbbctl version") - self.db.tbbctl_version = info[1].split()[-1] - - # check if image_nr > 0 for all boards - if str(info).count('V') != (self.nr * 4): - logger.warn("WARNING, Not all boards in working image") - - for tbb in self.db.tbb: - board_info = info[2+tbb.nr].strip().split(' ') - #print board_info - if board_info[3].split()[1] != tpV: - logger.warn("Board %d Not right TP version" %(tbb.nr)) - tbb.tp_version = board_info[3].split()[1] - - if board_info[4].split()[1] != mpV: - logger.warn("Board %d Not right MP version" %(tbb.nr)) - tbb.mp_version = board_info[4].split()[1] - logger.info("=== Done TBB Version check ===") - self.db.addTestDone('TV') - return - - # Check memory address and data lines - def checkMemory(self): - logger.info("=== TBB Memory check ===") - tbbctl('--free') - for tbb in self.db.tbb: - answer = tbbctl('--testddr=%d' %(tbb.nr)) - info = answer.splitlines()[-3:] - ok = True - if info[0].strip() != 'All Addresslines OK': - logger.warn("Board %d Addresline error" %(tbb.nr)) - ok = False - - if info[1].strip() != 'All Datalines OK': - logger.warn("Board %d Datalines error" %(tbb.nr)) - ok = False - - if not ok: - tbb.memory_ok = 0 - logger.info(answer) - # turn on recording again - tbbctl('--alloc') - tbbctl('--record') - rspctl('--tbbmode=transient') - logger.info("=== Done TBB Memory check ===") - self.db.addTestDone('TM') - return -#### end of cTBB class #### - - -# class for checking RSP boards using rspctl -class cRSP: - global logger - def __init__(self, db): - self.db = db - self.nr = self.db.nr_rsp - - # check software versions of driver, tbbctl and TP/MP firmware - def checkVersions(self, bpV, apV ): - logger.info("=== RSP Version check ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - answer = rspctl('--version') - - # check if Driver is available - if answer.find('No Response') > 0: - logger.warn("No RSPDriver") - self.db.rspdriver_version = 0 - else: - infolines = answer.splitlines() - info = infolines - - images_ok = True - # check if image_nr > 0 for all boards - if str(info).count('0.0') != 0: - logger.warn("WARNING, Not all boards in working image") - images_ok = False - - for rsp in self.db.rsp: - board_info = info[rsp.nr].split(',') - - if board_info[1].split()[3] != bpV: - logger.warn("Board %d Not right BP version" %(rsp.nr)) - rsp.bp_version = board_info[1].split()[3] - images_ok = False - - if board_info[2].split()[3] != apV: - logger.warn("Board %d Not right AP version" %(rsp.nr)) - rsp.ap_version = board_info[2].split()[3] - images_ok = False - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return (False) - - logger.info("=== Done RSP Version check ===") - self.db.addTestDone('RV') - return (images_ok) - - def checkBoard(self): - max_1_2 = 1.3 - min_1_2 = 1.1 - - max_2_5 = 2.6 - min_2_5 = 2.4 - - max_3_3 = 3.4 - min_3_3 = 3.1 - - ok = True - logger.info("=== RSP Board check ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return (False) - answer = rspctl('--status') - p2 = 0 - for rsp in self.db.rsp: - p1 = answer.find("RSP[%2d]" %(rsp.nr), p2) - p2 = answer.find("\n", p1) - d = [float(i.split(":")[1].strip()) for i in answer[p1+7:p2].split(',')] - if len(d) == 3: - logger.debug("RSP board %d: [1.2V]=%3.2fV, [2.5V]=%3.2fV, [3.3V]=%3.2fV" %(rsp.nr, d[0], d[1], d[2])) - rsp.voltage1_2 = d[0] - if not (min_1_2 <= d[0] <= max_1_2): - rsp.voltage_ok = 0 - logger.info("RSP board %d [1.2V]=%3.2fV" %(rsp.nr, d[0])) - rsp.voltage2_5 = d[1] - if not (min_2_5 <= d[1] <= max_2_5): - rsp.voltage_ok = 0 - logger.info("RSP board %d [2.5V]=%3.2fV" %(rsp.nr, d[1])) - rsp.voltage3_3 = d[2] - if not (min_3_3 <= d[2] <= max_3_3): - rsp.voltage_ok = 0 - logger.info("RSP board %d [3.3V]=%3.2fV" %(rsp.nr, d[2])) - - for rsp in self.db.rsp: - p1 = answer.find("RSP[%2d]" %(rsp.nr), p2) - p2 = answer.find("\n", p1) - d = [float(i.split(":")[1].strip()) for i in answer[p1+7:p2].split(',')] - if len(d) == 6: - logger.debug("RSP board %d temperatures: pcb=%3.1f, bp=%3.1f, ap0=%3.1f, ap1=%3.1f, ap2=%3.1f, ap3=%3.1f" %( - rsp.nr, - d[0], d[1], d[2], d[3], d[4], d[5])) - rsp.pcb_temp = d[0] - if d[0] > 45.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [pcb_temp]=%2.0f" %(rsp.nr, d[0])) - rsp.bp_temp = d[1] - if d[1] > 75.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [bp_temp]=%2.0f" %(rsp.nr, d[1])) - rsp.ap0_temp = d[2] - if d[2] > 75.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [ap0_temp]=%2.0f" %(rsp.nr, d[2])) - rsp.ap1_temp = d[3] - if d[3] > 75.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [ap1_temp]=%2.0f" %(rsp.nr, d[3])) - rsp.ap2_temp = d[4] - if d[4] > 75.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [ap2_temp]=%2.0f" %(rsp.nr, d[4])) - rsp.ap3_temp = d[5] - if d[5] > 75.0: - rsp.temp_ok = 0 - logger.info("RSP board %d [ap3_temp]=%2.0f" %(rsp.nr, d[5])) - logger.info("=== Done RSP Board check ===") - self.db.addTestDone('RBC') - return (ok) - -#### end of cRSP class #### - - -# class for testing LBA antennas -class cLBA: - #global logger - # mode='lba_low' or 'lba_high' - def __init__(self, db, lba): - self.db = db - self.lba = lba - self.rcudata = cRCUdata(self.lba.nr_antennas*2) - self.rcudata.setActiveRcus(self.lba.selectList()) - - # Average normal value = 150.000.000 (81.76 dBm) -3dB +3dB - # LOW/HIGH LIMIT is used for calculating mean value - self.lowLimit = -3.0 #dB - self.highLimit = 3.0 #dB - - # MEAN LIMIT is used to check if mean of all antennas is ok - self.meanLimit = 66.0 #dB - - def reset(self): - self.rcudata.reset() - self.rcudata.reset_masks() - self.rcudata.setActiveRcus(self.lba.selectList()) - - def turnOffAnt(self, ant_nr): - ant = self.lba.ant[ant_nr] - ant.x.rcu_off = 1 - ant.y.rcu_off = 1 - self.rcudata.setInActiveRcu(ant.x.rcu) - self.rcudata.setInActiveRcu(ant.y.rcu) - logger.info("turned off antenna %d RCU(%d,%d)" %(ant.nr_pvss, ant.x.rcu, ant.y.rcu)) - rspctl("--rcumode=0 --select=%d,%d" %(ant.x.rcu, ant.y.rcu), wait=2.0) - rspctl("--rcuenable=0 --select=%d,%d" %(ant.x.rcu, ant.y.rcu), wait=2.0) - return - - def set_mode(self, mode): - if self.db.rcumode != mode: - self.db.rcumode = mode - turnoffRCUs() - turnonRCUs(mode=mode, rcus=self.lba.selectList()) - self.lba.resetRcuState() - self.rcudata.reset() - - def record_data(self, rec_time, new_data=False): - if new_data or self.rcudata.isActiveRcusChanged() or self.rcudata.getRecTime() < rec_time: - logger.debug('record info changed') - self.rcudata.resetActiveRcusChanged() - self.rcudata.record(rec_time=rec_time) - - # check for oscillating tiles and turn off RCU - # stop one RCU each run - def checkOscillation(self, mode): - logger.info("=== Start %s oscillation test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=28.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - clean = False - while not clean: - if self.db.checkEndTime(duration=18.0) == False: - logger.warn("check stopped, end time reached") - return - - clean = True - self.record_data(rec_time=3) - - for pol_nr, pol in enumerate(('X', 'Y')): - # result is a sorted list on maxvalue - result = search_oscillation(data=self.rcudata, pol=pol, delta=6.0) - if len(result) > 1: - clean = False - ant, peaks_sum, n_peaks, ant_low = sorted(result[1:], reverse=True)[0] #result[1] - #ant = rcu / 2 - #ant_polarity = rcu % 2 - rcu = (ant * 2) + pol_nr - logger.info("RCU %d LBA %d Oscillation sum=%3.1f peaks=%d low=%3.1fdB" %\ - (rcu, self.lba.ant[ant].nr_pvss, peaks_sum, n_peaks, ant_low)) - self.turnOffAnt(ant) - if pol_nr == 0: - self.lba.ant[ant].x.osc = 1 - else: - self.lba.ant[ant].y.osc = 1 - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.oscillation_check_done = 1 - self.db.addTestDone('O%d' %(mode)) - logger.info("=== Done %s oscillation test ===" %(self.lba.label)) - return - - def checkNoise(self, mode, record_time, low_deviation, high_deviation, max_diff): - logger.info("=== Start %s noise test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=(record_time+100.0)) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - for ant in self.lba.ant: - if ant.x.rcu_off or ant.y.rcu_off: - logger.info("skip low-noise test for antenna %d, RCUs turned off" %(ant.nr)) - - self.record_data(rec_time=record_time) - - # result is a sorted list on maxvalue - low_noise, high_noise, jitter = search_noise(self.rcudata, 'XY', low_deviation, high_deviation, max_diff) - - for n in low_noise: - rcu, val, bad_secs, ref, diff = n - ant = rcu / 2 - if self.lba.ant[ant].x.rcu_off or self.lba.ant[ant].y.rcu_off: - continue - #self.turnOffAnt(ant) - logger.info("RCU %d Ant %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" %\ - (rcu, self.lba.ant[ant].nr_pvss, val, bad_secs, self.rcudata.frames, ref, diff)) - - self.rcudata.add_to_rcu_mask(rcu) - if rcu%2 == 0: - antenna = self.lba.ant[ant].x - else: - antenna = self.lba.ant[ant].y - - antenna.low_seconds += self.rcudata.frames - antenna.low_bad_seconds += bad_secs - if val < self.lba.ant[ant].x.low_val: - antenna.low_noise = 1 - antenna.low_val = val - antenna.low_ref = ref - antenna.low_diff = diff - - for n in high_noise: - rcu, val, bad_secs, ref, diff = n - ant = rcu / 2 - #self.turnOffAnt(ant) - logger.info("RCU %d Ant %d High-Noise value=%3.1f bad=%d(%d) ref=%3.1f diff=%3.1f" %\ - (rcu, self.lba.ant[ant].nr_pvss, val, bad_secs, self.rcudata.frames, ref, diff)) - - self.rcudata.add_to_rcu_mask(rcu) - if rcu%2 == 0: - antenna = self.lba.ant[ant].x - else: - antenna = self.lba.ant[ant].y - - antenna.high_seconds += self.rcudata.frames - antenna.high_bad_seconds += bad_secs - if val > self.lba.ant[ant].x.high_val: - antenna.high_noise = 1 - antenna.high_val = val - antenna.high_ref = ref - antenna.high_diff = diff - - for n in jitter: - rcu, val, ref, bad_secs = n - ant = rcu / 2 - logger.info("RCU %d Ant %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" %(rcu, self.lba.ant[ant].nr_pvss, val, ref)) - - self.rcudata.add_to_rcu_mask(rcu) - if rcu%2 == 0: - antenna = self.lba.ant[ant].x - else: - antenna = self.lba.ant[ant].y - - antenna.jitter_seconds += self.rcudata.frames - antenna.jitter_bad_seconds += bad_secs - if val > antenna.jitter_val: - antenna.jitter = 1 - antenna.jitter_val = val - antenna.jitter_ref = ref - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.noise_check_done = 1 - self.db.addTestDone('NS%d=%d' %(mode, record_time)) - logger.info("=== Done %s noise test ===" %(self.lba.label)) - return - - def checkSpurious(self, mode): - logger.info("=== Start %s spurious test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=12.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - self.record_data(rec_time=3) - - # result is a sorted list on maxvalue - result = search_spurious(self.rcudata, 'XY', delta=3.0) - for rcu in result: - ant = rcu / 2 - ant_polarity = rcu % 2 - #self. turnOffAnt(ant) - logger.info("RCU %d Ant %d pol %d Spurious" %(rcu, self.lba.ant[ant].nr_pvss, ant_polarity)) - - self.rcudata.add_to_rcu_mask(rcu) - if ant_polarity == 0: - self.lba.ant[ant].x.spurious = 1 - else: - self.lba.ant[ant].y.spurious = 1 - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.spurious_check_done = 1 - self.db.addTestDone('SP%d' %(mode)) - logger.info("=== Done %s spurious test ===" %(self.lba.label)) - return - - def checkShort(self, mode): - logger.info("=== Start %s Short test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=15.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - self.record_data(rec_time=3) - - # search for shorted cable (input), mean signal all subbands between 55 and 61 dB - logger.debug("Check Short") - short = searchShort(self.rcudata) - for i in short: - rcu, mean_val = i - ant = rcu / 2 - pol = rcu % 2 - - logger.info("%s %2d RCU %3d Short, mean value band=%5.1fdB" %\ - (self.lba.label, self.lba.ant[ant].nr_pvss, rcu, mean_val)) - - self.rcudata.add_to_rcu_mask(rcu) - if pol == 0: - self.lba.ant[ant].x.short = 1; - self.lba.ant[ant].x.short_val = mean_val; - else: - self.lba.ant[ant].y.short = 1; - self.lba.ant[ant].y.short_val = mean_val; - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.short_check_done = 1 - self.db.addTestDone('SH%d' %(mode)) - logger.info("=== Done %s Short test ===" %(self.lba.label)) - return - - def checkFlat(self, mode): - logger.info("=== Start %s Flat test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=15.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - self.record_data(rec_time=3) - - # search for flatliners, mean signal all subbands between 63 and 65 dB - logger.debug("Check Flat") - flat = searchFlat(self.rcudata) - for i in flat: - rcu, mean_val = i - ant = rcu / 2 - pol = rcu % 2 - - logger.info("%s %2d RCU %3d Flat, mean value band=%5.1fdB" %\ - (self.lba.label, self.lba.ant[ant].nr_pvss, rcu, mean_val)) - - self.rcudata.add_to_rcu_mask(rcu) - if pol == 0: - self.lba.ant[ant].x.flat = 1; - self.lba.ant[ant].x.flat_val = mean_val; - else: - self.lba.ant[ant].y.flat = 1; - self.lba.ant[ant].y.flat_val = mean_val; - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.flat_check_done = 1 - self.db.addTestDone('F%d' %(mode)) - logger.info("=== Done %s Flat test ===" %(self.lba.label)) - return - - - def checkDown(self, mode, subband): - logger.info("=== Start %s Down test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=15.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - self.record_data(rec_time=3) - - # mark lba as down if top of band is lower than normal and top is shifted more than 10 subbands to left or right - logger.debug("Check Down") - down, shifted = searchDown(self.rcudata, subband) - for i in down: - ant, max_x_sb, max_y_sb, mean_max_sb = i - max_x_offset = max_x_sb - mean_max_sb - max_y_offset = max_y_sb - mean_max_sb - - if self.lba.ant[ant].x.flat or self.lba.ant[ant].x.short or self.lba.ant[ant].y.flat or self.lba.ant[ant].y.short: - continue - - self.lba.ant[ant].x.offset = max_x_offset - self.lba.ant[ant].y.offset = max_y_offset - self.lba.ant[ant].down = 1 - logger.info("%s %2d RCU %3d/%3d Down, offset-x=%d offset-y=%d" %\ - (self.lba.label, self.lba.ant[ant].nr_pvss, self.lba.ant[ant].x.rcu, self.lba.ant[ant].y.rcu, max_x_offset, max_y_offset)) - self.rcudata.add_to_rcu_mask(self.lba.ant[ant].x.rcu) - self.rcudata.add_to_rcu_mask(self.lba.ant[ant].y.rcu) - for i in shifted: - rcu, max_sb, mean_max_sb = i - ant = rcu / 2 - logger.info("%s %2d RCU %3d shifted top on sb=%d, normal=sb%d" %(self.lba.label, self.lba.ant[ant].nr_pvss, rcu, max_sb, mean_max_sb)) - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.down_check_done = 1 - self.db.addTestDone('D%d' %(mode)) - logger.info("=== Done %s Down test ===" %(self.lba.label)) - return - - - def checkSignal(self, mode, subband, min_signal, low_deviation, high_deviation): - logger.info("=== Start %s RF test ===" %(self.lba.label)) - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=15.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - self.record_data(rec_time=3) - - self.rcudata.searchTestSignal(subband=subband, minsignal=min_signal, maxsignal=90.0) - - logger.info("For X used test subband=%d (%3.1f dB) in mode %d" %\ - (self.rcudata.testSubband_X, self.rcudata.testSignal_X, mode)) - logger.info("For Y used test subband=%d (%3.1f dB) in mode %d" %\ - (self.rcudata.testSubband_Y, self.rcudata.testSignal_Y, mode)) - - if self.rcudata.testSubband_X == 0 or self.rcudata.testSubband_Y == 0: - logger.warn("LBA mode %d, No test signal found" %(mode)) - return - - ssdataX = self.rcudata.getSubbandX() - ssdataY = self.rcudata.getSubbandY() - #if np.ma.count(ssdataX) == 0 or np.ma.count(ssdataY) == 0: - # all zeros (missing settings!!) - # return - - # use only values between lowLimit and highLimit for average calculations - dataInBandX = np.ma.masked_outside(ssdataX, (self.rcudata.testSignal_X + self.lowLimit), (self.rcudata.testSignal_X + self.highLimit)) - medianValX = np.ma.median(dataInBandX) - - dataInBandY = np.ma.masked_outside(ssdataY, (self.rcudata.testSignal_Y + self.lowLimit), (self.rcudata.testSignal_Y + self.highLimit)) - medianValY = np.ma.median(dataInBandY) - - logger.info("used medianValX=%f" %(medianValX)) - logger.info("used medianValY=%f" %(medianValY)) - if medianValX < self.meanLimit or medianValY < self.meanLimit: - self.lba.avg_2_low = 1 - self.lba.avg_x = medianValX - self.lba.avg_y = medianValY - - self.lba.test_signal_x = medianValX - self.lba.test_signal_y = medianValY - self.lba.test_subband_x = self.rcudata.testSubband_X - self.lba.test_subband_y = self.rcudata.testSubband_Y - - logger.debug("Check RF signal") - for ant in self.lba.ant: - ant.x.test_signal = ssdataX[ant.nr] - ant.y.test_signal = ssdataY[ant.nr] - - loginfo = False - if ssdataX[ant.nr] < (medianValX + low_deviation): - if not max(ant.x.flat, ant.x.short, ant.down): - ant.x.too_low = 1 - if ssdataX[ant.nr] < 2.0: - ant.x.rcu_error = 1 - loginfo = True - - if ssdataX[ant.nr] > (medianValX + high_deviation): - ant.x.too_high = 1 - loginfo = True - - if ssdataY[ant.nr] < (medianValY + low_deviation): - if not max(ant.y.flat, ant.y.short, ant.down): - ant.y.too_low = 1 - if ssdataY[ant.nr] < 2.0: - ant.y.rcu_error = 1 - loginfo = True - - if ssdataY[ant.nr] > (medianValY + high_deviation): - ant.y.too_high = 1 - loginfo = True - - if loginfo: - logger.info("%s %2d RCU %3d/%3d X=%5.1fdB Y=%5.1fdB" %(self.lba.label, ant.nr_pvss, ant.x.rcu, ant.y.rcu, ssdataX[ant.nr], ssdataY[ant.nr])) - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.lba.signal_check_done = 1 - self.db.addTestDone('S%d' %(mode)) - logger.info("=== Done %s RF test ===" %(self.lba.label)) - return -#### end of cLBA class #### - - -# class for testing HBA antennas -class cHBA: - #global logger - def __init__(self, db, hba): - self.db = db - self.hba = hba - self.rcudata = cRCUdata(hba.nr_tiles*2) - self.rcudata.setActiveRcus(self.hba.selectList()) - self.rcumode = 0 - - def reset(self): - self.rcudata.reset() - self.rcudata.reset_masks() - self.rcudata.setActiveRcus(self.hba.selectList()) - - def turnOnTiles(self): - pass - - def turnOffTile(self, tile_nr): - tile = self.hba.tile[tile_nr] - tile.x.rcu_off = 1 - tile.y.rcu_off = 1 - logger.info("turned off tile %d RCU(%d,%d)" %(tile.nr, tile.x.rcu, tile.y.rcu)) - rspctl("--rcumode=0 --select=%d,%d" %(tile.x.rcu, tile.y.rcu), wait=2.0) - return - - def turnOffBadTiles(self): - for tile in self.hba.tile: - if tile.x.rcu_off and tile.y.rcu_off: - continue - no_modem = 0 - modem_error = 0 - for elem in tile.element: - if elem.no_modem: - no_modem += 1 - if elem.modem_error: - modem_error += 1 - if tile.x.osc or tile.y.osc or (no_modem >= 8) or (modem_error >= 8): - self.turnOffTile(tile.nr) - return - - def set_mode(self, mode): - if self.db.rcumode != mode: - self.db.rcumode = mode - turnoffRCUs() - turnonRCUs(mode=mode, rcus=self.hba.selectList()) - self.hba.resetRcuState() - self.rcudata.reset() - - def record_data(self, rec_time, new_data=False): - if new_data or self.rcudata.isActiveRcusChanged() or self.rcudata.getRecTime() < rec_time: - logger.debug('record info changed') - self.rcudata.resetActiveRcusChanged() - self.rcudata.record(rec_time=rec_time) - - def checkModem(self, mode): - # setup internal test db - n_elements = 16 - n_tests = 7 - modem_tst = list() - for tile_nr in range(self.db.nr_hba): - tile = list() - for elem_nr in range(n_elements): - test = list() - for tst_nr in range(n_tests): - test.append([0, 0]) - tile.append(test) - modem_tst.append(tile) - # done - - logger.info("=== Start HBA modem test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=50.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - time.sleep(4.0) - ctrlstr = list() - ctrlstr.append(('129,'* 16)[:-1]) # 0ns - ctrlstr.append(('133,'* 16)[:-1]) # 0.5ns - ctrlstr.append(('137,'* 16)[:-1]) # 1ns - ctrlstr.append(('145,'* 16)[:-1]) # 2ns - ctrlstr.append(('161,'* 16)[:-1]) # 4ns - ctrlstr.append(('193,'* 16)[:-1]) # 8ns - ctrlstr.append(('253,'* 16)[:-1]) # 15.5ns - #rsp_hba_delay(delay=ctrlstr[6], rcus=self.hba.selectList(), discharge=False) - tst_nr = 0 - for ctrl in ctrlstr: - - rsp_hba_delay(delay=ctrl, rcus=self.hba.selectList(), discharge=False) - data = rspctl('--realdelays', wait=1.0).splitlines() - - ctrllist = ctrl.split(',') - for line in data: - if line[:3] == 'HBA': - rcu = int(line[line.find('[')+1:line.find(']')]) - hba_nr = rcu / 2 - if self.hba.tile[hba_nr].on_bad_list: - continue - ant_polarity = rcu % 2 - realctrllist = line[line.find('=')+1:].strip().split() - for elem in self.hba.tile[hba_nr].element: - if ctrllist[elem.nr-1] != realctrllist[elem.nr-1]: - logger.info("Modemtest Tile=%d RCU=%d Element=%d ctrlword=%s response=%s" %\ - (hba_nr, rcu, elem.nr, ctrllist[elem.nr-1], realctrllist[elem.nr-1])) - - if realctrllist[elem.nr-1].count('?') == 3: - #elem.no_modem += 1 - modem_tst[hba_nr][elem.nr-1][tst_nr][0] = 1 - else: - #elem.modem_error += 1 - modem_tst[hba_nr][elem.nr-1][tst_nr][1] = 1 - tst_nr += 1 - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - # analyse test results and add to DB - no_modem = dict() - modem_error = dict() - for tile_nr in range(self.db.nr_hba): - n_no_modem = dict() - n_modem_error = dict() - for elem_nr in range(n_elements): - n_no_modem[elem_nr] = 0 - n_modem_error[elem_nr] = 0 - for tst_nr in range(n_tests): - if modem_tst[tile_nr][elem_nr][tst_nr][0]: - n_no_modem[elem_nr] += 1 - if modem_tst[tile_nr][elem_nr][tst_nr][1]: - n_modem_error[elem_nr] += 1 - no_modem[tile_nr] = n_no_modem - modem_error[tile_nr] = n_modem_error - - n_tile_err = 0 - for tile in no_modem: - n_elem_err = 0 - for elem in no_modem[tile]: - if no_modem[tile][elem] == n_tests: - n_elem_err += 1 - if n_elem_err == n_elements: - n_tile_err += 1 - - if n_tile_err < (self.db.nr_hba / 2): - for tile_nr in range(self.db.nr_hba): - for elem_nr in range(n_elements): - if no_modem[tile_nr][elem_nr] >= 2: # 2 or more ctrl values went wrong - self.db.hba.tile[tile_nr].element[elem_nr].no_modem = 1 - - n_tile_err = 0 - for tile in modem_error: - n_elem_err = 0 - for elem in modem_error[tile]: - if modem_error[tile][elem] == n_tests: - n_elem_err += 1 - if n_elem_err == n_elements: - n_tile_err += 1 - - if n_tile_err < (self.db.nr_hba / 2): - for tile_nr in range(self.db.nr_hba): - for elem_nr in range(n_elements): - if no_modem[tile_nr][elem_nr] >= 2: # 2 or more ctrl values went wrong - self.db.hba.tile[tile_nr].element[elem_nr].modem_error = 1 - - self.hba.modem_check_done = 1 - self.db.addTestDone('M%d' % mode) - logger.info("=== Done HBA modem test ===") - #self.db.rcumode = 0 - return - - # check for summator noise and turn off RCU - def checkSummatorNoise(self, mode): - logger.info("=== Start HBA tile based summator-noise test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=25.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - delay_str = ('253,'* 16)[:-1] - rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - self.record_data(rec_time=12) - - for pol_nr, pol in enumerate(('X', 'Y')): - sum_noise, cable_reflection = search_summator_noise(data=self.rcudata, pol=pol, min_peak=0.8) - for n in sum_noise: - bin_nr, cnt, n_peaks = n - tile = bin_nr - logger.info("RCU %d Tile %d Summator-Noise cnt=%3.1f peaks=%3.1f" %(self.hba.tile[tile].x.rcu, tile, cnt, n_peaks)) - if pol == 'X': - self.hba.tile[tile].x.summator_noise = 1 - else: - self.hba.tile[tile].y.summator_noise = 1 - self.turnOffTile(tile) - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.summatornoise_check_done = 1 - self.db.addTestDone('SN%d' %(mode)) - logger.info("=== Done HBA tile based summator-noise test ===") - return - - # check for oscillating tiles and turn off RCU - # stop one RCU each run - def checkOscillation(self, mode): - logger.info("=== Start HBA tile based oscillation test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=35.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - delay_str = ('253,'* 16)[:-1] - get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - clean = False - while not clean: - if self.db.checkEndTime(duration=25.0) == False: - logger.warn("check stopped, end time reached") - return - - clean = True - - self.record_data(rec_time=12, new_data=get_new_data) - - for pol_nr, pol in enumerate(('X', 'Y')): - # result is a sorted list on maxvalue - result = search_oscillation(data=self.rcudata, pol=pol, delta=6.0) # start_sb=45, stop_sb=350 - if len(result) > 1: - if len(result) == 2: - tile, max_sum, n_peaks, rcu_low = result[1] - else: - ref_low = result[0][3] - max_low_tile = (-1, -1) - max_sum_tile = (-1, -1) - for i in result[1:]: - tile, max_sum, n_peaks, tile_low = i - #rcu = (tile * 2) + pol_nr - if max_sum > max_sum_tile[0]: - max_sum_tile = (max_sum, tile) - if (tile_low - ref_low) > max_low_tile[0]: - max_low_tile = (tile_low, tile) - - rcu_low, tile = max_low_tile - - clean = False - get_new_data = True - #tile = rcu / 2 - #tile_polarity = rcu % 2 - rcu = (tile * 2) + pol_nr - logger.info("RCU %d Tile %d Oscillation sum=%3.1f peaks=%d low=%3.1f" %\ - (rcu, tile, max_sum, n_peaks, tile_low)) - self.turnOffTile(tile) - if pol_nr == 0: - self.hba.tile[tile].x.osc = 1 - else: - self.hba.tile[tile].y.osc = 1 - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.oscillation_check_done = 1 - self.db.addTestDone('O%d' %(mode)) - logger.info("=== Done HBA tile based oscillation test ===") - return - - def checkNoise(self, mode, record_time, low_deviation, high_deviation, max_diff): - logger.info("=== Start HBA tile based noise test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=(record_time+60.0)) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - logger.info("skip low-noise test for tile %d, RCUs turned off" %(tile.nr)) - - delay_str = ('253,'* 16)[:-1] - get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - self.record_data(rec_time=record_time, new_data=get_new_data) - - for pol_nr, pol in enumerate(('X', 'Y')): - # result is a sorted list on maxvalue - low_noise, high_noise, jitter = search_noise(self.rcudata, pol, low_deviation, high_deviation, max_diff) - - for n in low_noise: - bin_nr, val, bad_secs, ref, diff = n - tile = bin_nr - rcu = (tile * 2) + pol_nr - if self.hba.tile[tile].x.rcu_off or self.hba.tile[tile].y.rcu_off: - continue - logger.info("RCU %d Tile %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" %\ - (rcu, tile, val, bad_secs, self.rcudata.frames, ref, diff)) - - if pol == 'X': - tile_polarity = self.hba.tile[tile].x - else: - tile_polarity = self.hba.tile[tile].y - - tile_polarity.low_seconds += self.rcudata.frames - tile_polarity.low_bad_seconds += bad_secs - if val < tile_polarity.low_val: - tile_polarity.low_noise = 1 - tile_polarity.low_val = val - tile_polarity.low_ref = ref - tile_polarity.low_diff = diff - - for n in high_noise: - bin_nr, val, bad_secs, ref, diff = n - tile = bin_nr - rcu = (tile * 2) + pol_nr - logger.info("RCU %d Tile %d High-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.1f" %\ - (rcu, tile, val, bad_secs, self.rcudata.frames, ref, diff)) - - if pol == 'X': - tile_polarity = self.hba.tile[tile].x - else: - tile_polarity = self.hba.tile[tile].y - - tile_polarity.high_seconds += self.rcudata.frames - tile_polarity.high_bad_seconds += bad_secs - if val > tile_polarity.high_val: - tile_polarity.high_noise = 1 - tile_polarity.high_val = val - tile_polarity.high_ref = ref - tile_polarity.high_diff = diff - - for n in jitter: - bin_nr, val, ref, bad_secs = n - tile = bin_nr - rcu = (tile * 2) + pol_nr - logger.info("RCU %d Tile %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" %(rcu, tile, val, ref)) - - if pol == 'X': - tile_polarity = self.hba.tile[tile].x - else: - tile_polarity = self.hba.tile[tile].y - - tile_polarity.jitter_seconds += self.rcudata.frames - tile_polarity.jitter_bad_seconds += bad_secs - if val > tile_polarity.jitter_val: - tile_polarity.jitter = 1 - tile_polarity.jitter_val = val - tile_polarity.jitter_ref = ref - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.noise_check_done = 1 - self.db.addTestDone('NS%d=%d' %(mode, record_time)) - logger.info("=== Done HBA tile based noise test ===") - return - - def checkSpurious(self, mode): - logger.info("=== Start HBA tile based spurious test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=12.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - delay_str = ('253,'* 16)[:-1] - get_new_data = rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - self.record_data(rec_time=12, new_data=get_new_data) - - for pol_nr, pol in enumerate(('X', 'Y')): - # result is a sorted list on maxvalue - result = search_spurious(self.rcudata, pol, delta=3.0) - for bin_nr in result: - tile = bin_nr - rcu = (tile * 2) + pol_nr - logger.info("RCU %d Tile %d pol %c Spurious" %(rcu, tile, pol)) - if pol == 'X': - self.hba.tile[tile].x.spurious = 1 - else: - self.hba.tile[tile].y.spurious = 1 - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.spurious_check_done = 1 - self.db.addTestDone('SP%d' %(mode)) - logger.info("=== Done HBA spurious test ===") - return - - def checkSignal(self, mode, subband, min_signal, low_deviation, high_deviation): - logger.info("=== Start HBA tile based RF test ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - if self.db.checkEndTime(duration=37.0) == False: - logger.warn("check stopped, end time reached") - return - - self.set_mode(mode) - - # check twice - # 2 ... check if all elements are turned off, normal value between 60.0 and 62.0 - # 128 ... - # 253 ... - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - logger.info("skip signal test for tile %d, RCUs turned off" %(tile.nr)) - - logger.info("start test") - for ctrl in ('2,', '128,', '253,'): - if self.db.checkEndTime(duration=20.0) == False: - logger.warn("check stopped, end time reached") - return - - if ctrl == '128,': ctrl_nr = 0 - elif ctrl == '253,': ctrl_nr = 1 - - logger.info("HBA signal test, ctrl word %s" %(ctrl[:-1])) - - delay_str = (ctrl*16)[:-1] - rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - self.record_data(rec_time=2, new_data=True) - self.rcudata.searchTestSignal(subband=subband, minsignal=min_signal, maxsignal=150.0) - logger.info("HBA, X used test subband=%d avg_signal=%3.1f" %(self.rcudata.testSubband_X, self.rcudata.testSignal_X)) - logger.info("HBA, Y used test subband=%d avg_signal=%3.1f" %(self.rcudata.testSubband_Y, self.rcudata.testSignal_Y)) - - if ctrl == '2,': - ssdataX = self.rcudata.getSubbandX(subband=subband) - ssdataY = self.rcudata.getSubbandY(subband=subband) - else: - ssdataX = self.rcudata.getSubbandX() - ssdataY = self.rcudata.getSubbandY() - - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - continue - logger.debug("HBA Tile=%d : X=%3.1fdB Y=%3.1fdB" %\ - (tile.nr, ssdataX[tile.nr], ssdataY[tile.nr])) - if ctrl == '2,': - continue - - if (self.rcudata.testSignal_X != -1) and (self.rcudata.testSignal_Y != -1): - - self.hba.ref_signal_x[ctrl_nr] = self.rcudata.testSignal_X - self.hba.ref_signal_y[ctrl_nr] = self.rcudata.testSignal_Y - self.hba.test_subband_x[ctrl_nr] = self.rcudata.testSubband_X - self.hba.test_subband_y[ctrl_nr] = self.rcudata.testSubband_Y - - avgX = self.rcudata.testSignal_X - avgY = self.rcudata.testSignal_Y - minX = ssdataX.min() - minY = ssdataY.min() - - # if all elements in range - #if minX < (avgX + self.min_dB) and minY < (avgY + self.min_dB): - # continue - - logger.debug("X data: min=%5.3f max=%5.3f avg=%5.3f" %(minX, ssdataX.max(), avgX)) - logger.debug("Y data: min=%5.3f max=%5.3f avg=%5.3f" %(minY, ssdataY.max(), avgY)) - - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - continue - - tile.x.test_signal[ctrl_nr] = ssdataX[tile.nr] - tile.y.test_signal[ctrl_nr] = ssdataY[tile.nr] - - loginfo = False - if ssdataX[tile.nr] < (avgX + low_deviation): - if ssdataX[tile.nr] < 2.0: - tile.x.no_signal = 1 - elif ssdataX[tile.nr] > 55.0 and ssdataX[tile.nr] < 65.0: - tile.no_power = 1 - else: - tile.x.too_low = 1 - loginfo = True - - if ssdataX[tile.nr] > (avgX + high_deviation): - tile.x.too_high = 1 - loginfo = True - - if ssdataY[tile.nr] < (avgY + low_deviation): - if ssdataY[tile.nr] < 2.0: - tile.y.no_signal = 1 - elif ssdataY[tile.nr] > 55.0 and ssdataY[tile.nr] < 65.0: - tile.no_power = 1 - else: - tile.y.too_low = 1 - loginfo = True - - if ssdataY[tile.nr] > (avgY + high_deviation): - tile.y.too_high = 1 - loginfo = True - - if loginfo: - logger.info("HBA Tile=%d Error: X=%3.1fdB Y=%3.1fdB" %\ - (tile.nr, ssdataX[tile.nr], ssdataY[tile.nr])) - else: - logger.warn("HBA, No valid test signal") - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.signal_check_done = 1 - self.db.addTestDone('S%d' %(mode)) - logger.info("=== Done HBA signal test ===") - return - - # Next tests are element based - # - # 8bit control word - # - # bit-7 RF on/off 1 = on - # bit-6 delay 1 = 8 ns - # bit-5 delay 1 = 4 ns - # bit-4 delay 1 = 2 ns - # bit-3 delay 1 = 1 ns - # bit-2 delay 1 = 0.5 ns - # bit-1 LNA on/off 1 = off - # bit-0 LED on/off 1 = on - # - # control word = 0 (signal - 30 db) - # control word = 2 (signal - 40 db) - # - def checkElements(self, mode, record_time, subband, - noise_low_deviation, noise_high_deviation, noise_max_diff, - rf_min_signal, rf_low_deviation, rf_high_deviation): - - logger.info("=== Start HBA element based tests ===") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - - self.set_mode(mode) - - n_rcus_off = 0 - for ctrl in ('128', '253'): - if ctrl == '128': ctrl_nr = 0 - elif ctrl == '253': ctrl_nr = 1 - - for elem in range(self.hba.tile[0].nr_elements): - logger.info("check elements %d, ctrlword=%s" %(elem+1, ctrl)) - - if self.db.checkEndTime(duration=45.0) == False: - logger.warn("check stopped, end time reached") - return - - if n_rcus_off > 0: - rsp_rcu_mode(mode=mode, rcus=self.hba.selectList()) - n_rcus_off = 0 - for tile in self.hba.tile: - if tile.element[elem].no_modem or tile.element[elem].modem_error: - self.turnOffTile(tile.nr) - n_rcus_off += 1 - logger.info("skip tile %d, modem error" %(tile.nr)) - - delay_str = ('2,'*elem + ctrl + ',' + '2,'*15)[:33] - rsp_hba_delay(delay=delay_str, rcus=self.hba.selectList()) - - clean = False - while not clean: - if self.db.checkEndTime(duration=(record_time+45.0)) == False: - logger.warn("check stopped, end time reached") - return - - clean = True - self.record_data(rec_time=record_time, new_data=True) - - clean, n_off = self.checkOscillationElements(elem) - n_rcus_off += n_off - if n_off > 0: continue - n_off = self.checkSpuriousElements(elem) - n_rcus_off += n_off - if n_off > 0: continue - self.checkNoiseElements(elem, noise_low_deviation, noise_high_deviation, noise_max_diff) - self.checkSignalElements(elem, ctrl_nr, subband, rf_min_signal, rf_low_deviation, rf_high_deviation) - - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down while testing, skip result") - return - - self.hba.element_check_done = 1 - self.db.addTestDone('E%d' %(mode)) - logger.info("=== Done HBA element tests ===") - return - - # check for oscillating tiles and turn off RCU - # stop one RCU each run - # elem counts from 0..15 (for user output use 1..16) - def checkOscillationElements(self, elem): - logger.info("--- oscillation test --") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - clean = True - n_rcus_off = 0 - # result is a sorted list on maxvalue - result = search_oscillation(data=self.rcudata, pol='XY', delta=3.0) - if len(result) > 1: - clean = False - rcu, peaks_sum, n_peaks, rcu_low = sorted(result[1:], reverse=True)[0] #result[1] - tile = rcu / 2 - if self.hba.tile[tile].element[elem].no_modem or self.hba.tile[tile].element[elem].modem_error: - return(True, 0) - tile_polarity = rcu % 2 - logger.info("RCU %d Tile %d Element %d Oscillation sum=%3.1f peaks=%d, low=%3.1f" %\ - (rcu, tile, elem+1, peaks_sum, n_peaks, rcu_low)) - self.turnOffTile(tile) - n_rcus_off += 1 - if tile_polarity == 0: - self.hba.tile[tile].element[elem].x.osc = 1 - else: - self.hba.tile[tile].element[elem].y.osc = 1 - return (clean, n_rcus_off) - - def checkSpuriousElements(self, elem): - logger.info("--- spurious test ---") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - n_rcus_off = 0 - # result is a sorted list on maxvalue - result = search_spurious(data=self.rcudata, pol='XY', delta=3.0) - for rcu in result: - tile = rcu / 2 - tile_polarity = rcu % 2 - logger.info("RCU %d Tile %d Element %d pol %d Spurious" %(rcu, tile, elem+1, tile_polarity)) - self.turnOffTile(tile) - n_rcus_off += 1 - if tile_polarity == 0: - self.hba.tile[tile].element[elem].x.spurious = 1 - else: - self.hba.tile[tile].element[elem].y.spurious = 1 - return (n_rcus_off) - - def checkNoiseElements(self, elem, low_deviation, high_deviation, max_diff): - logger.info("--- noise test ---") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - # result is a sorted list on maxvalue - low_noise, high_noise, jitter = search_noise(self.rcudata, 'XY', low_deviation, high_deviation, max_diff) - - for n in low_noise: - rcu, val, bad_secs, ref, diff = n - tile = rcu / 2 - logger.info("RCU %d Tile %d Element %d Low-Noise value=%3.1f bad=%d(%d) limit=%3.1f diff=%3.3f" %\ - (rcu, tile, elem+1, val, bad_secs, self.rcudata.frames, ref, diff)) - - if rcu%2 == 0: - elem_polarity = self.hba.tile[tile].element[elem].x - else: - elem_polarity = self.hba.tile[tile].element[elem].y - - elem_polarity.low_seconds += self.rcudata.frames - elem_polarity.low_bad_seconds += bad_secs - if val < elem_polarity.low_val: - elem_polarity.low_noise = 1 - elem_polarity.low_val = val - elem_polarity.low_ref = ref - elem_polarity.low_diff = diff - - for n in high_noise: - rcu, val, bad_secs, ref, diff = n - tile = rcu / 2 - logger.info("RCU %d Tile %d Element %d High-Noise value=%3.1f bad=%d(%d) ref=%3.1f diff=%3.1f" %\ - (rcu, tile, elem+1, val, bad_secs, self.rcudata.frames, ref, diff)) - - if rcu%2 == 0: - elem_polarity = self.hba.tile[tile].element[elem].x - else: - elem_polarity = self.hba.tile[tile].element[elem].y - - elem_polarity.high_seconds += self.rcudata.frames - elem_polarity.high_bad_seconds += bad_secs - if val > elem_polarity.high_val: - elem_polarity.high_noise = 1 - elem_polarity.high_val = val - elem_polarity.high_ref = ref - elem_polarity.high_diff = diff - - for n in jitter: - rcu, val, ref, bad_secs = n - tile = rcu / 2 - logger.info("RCU %d Tile %d Element %d Jitter, fluctuation=%3.1fdB normal=%3.1fdB" %\ - (rcu, tile, elem+1, val, ref)) - - if rcu%2 == 0: - elem_polarity = self.hba.tile[tile].element[elem].x - else: - elem_polarity = self.hba.tile[tile].element[elem].y - - elem_polarity.jitter_seconds += self.rcudata.frames - elem_polarity.jitter_bad_seconds += bad_secs - if val > elem_polarity.jitter_val: - elem_polarity.jitter = 1 - elem_polarity.jitter_val = val - elem_polarity.jitter_ref = ref - return - - def checkSignalElements(self, elem, ctrl_nr, subband, min_signal, low_deviation, high_deviation): - - logger.info("--- RF test ---") - if not checkActiveRSPDriver(): - logger.warn("RSPDriver down, skip test") - return - self.rcudata.searchTestSignal(subband=subband, minsignal=min_signal, maxsignal=120.0) - logger.info("HBA, X used test subband=%d avg_signal=%3.1f" %(self.rcudata.testSubband_X, self.rcudata.testSignal_X)) - logger.info("HBA, Y used test subband=%d avg_signal=%3.1f" %(self.rcudata.testSubband_Y, self.rcudata.testSignal_Y)) - - ssdataX = self.rcudata.getSubbandX() - ssdataY = self.rcudata.getSubbandY() - avgX = self.rcudata.testSignal_X - avgY = self.rcudata.testSignal_Y - minX = ssdataX.min() - minY = ssdataY.min() - - logger.debug("X data: min=%5.3f max=%5.3f avg=%5.3f" %(minX, ssdataX.max(), avgX)) - logger.debug("Y data: min=%5.3f max=%5.3f avg=%5.3f" %(minY, ssdataY.max(), avgY)) - - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - logger.info("skip signal test for tile %d, RCUs are turned off" %(tile.nr)) - - if self.rcudata.testSubband_X == 0 or self.rcudata.testSubband_Y == 0: - logger.warn("HBA, No valid test signal") - for tile in self.hba.tile: - tile.element[elem].x.ref_signal[ctrl_nr] = 0 - tile.element[elem].y.ref_signal[ctrl_nr] = 0 - return - - for tile in self.hba.tile: - if tile.x.rcu_off or tile.y.rcu_off: - continue - tile.element[elem].x.ref_signal[ctrl_nr] = avgX - tile.element[elem].y.ref_signal[ctrl_nr] = avgY - tile.element[elem].x.test_subband[ctrl_nr] = self.rcudata.testSubband_X - tile.element[elem].y.test_subband[ctrl_nr] = self.rcudata.testSubband_Y - tile.element[elem].x.test_signal[ctrl_nr] = ssdataX[tile.nr] - tile.element[elem].y.test_signal[ctrl_nr] = ssdataY[tile.nr] - - #logger.debug("HBA Tile=%d Element=%d: X=%3.1fdB Y=%3.1fdB" %\ - # (tile.nr, elem+1, ssdataX[tile.nr], ssdataY[tile.nr])) - - loginfo = False - if ssdataX[tile.nr] < (avgX + low_deviation): - if ssdataX[tile.nr] < 2.0: - tile.element[elem].x.no_signal = 1 - elif ssdataX[tile.nr] > 55.0 and ssdataX[tile.nr] < 65.0: - tile.element[elem].no_power = 1 - else: - tile.element[elem].x.too_low = 1 - loginfo = True - - if ssdataX[tile.nr] > (avgX + high_deviation): - tile.element[elem].x.too_high = 1 - loginfo = True - - if ssdataY[tile.nr] < (avgY + low_deviation): - if ssdataY[tile.nr] < 2.0: - tile.element[elem].y.no_signal = 1 - elif ssdataY[tile.nr] > 55.0 and ssdataY[tile.nr] < 65.0: - tile.element[elem].no_power = 1 - else: - tile.element[elem].y.too_low = 1 - loginfo = True - - if ssdataY[tile.nr] > (avgY + high_deviation): - tile.element[elem].y.too_high = 1 - loginfo = True - - if loginfo: - logger.info("HBA Tile=%d Element=%d Error: X=%3.1fdB Y=%3.1fdB" %\ - (tile.nr, elem+1, ssdataX[tile.nr], ssdataY[tile.nr])) - return -#### end of cHBA class #### - - diff --git a/LCU/checkhardware/rtsm.py b/LCU/checkhardware/rtsm.py index 1f9d114fd00bea01424e06ec2d4a8f69c238c06e..a390bc979f3deeaee2152f9ad45a105555240bee 100755 --- a/LCU/checkhardware/rtsm.py +++ b/LCU/checkhardware/rtsm.py @@ -2,235 +2,142 @@ check_version = '0714' -from threading import Thread import sys -import traceback import os -import numpy as np import time -#import datetime +import traceback +from socket import gethostname +from threading import Thread +import numpy as np + +os.umask(001) +os.nice(15) + +conf_file = r'/localhome/stationtest/config/check_hardware.conf' + +mainpath = r'/opt/stationtest' +maindatapath = r'/localhome/stationtest' +observationspath = r'/opt/lofar/var/run' +BEAMLETPATH = r'/localhome/data/Beamlets' + +confpath = os.path.join(maindatapath, 'config') +logpath = os.path.join(maindatapath, 'log') +rtsmpath = os.path.join(maindatapath, 'rtsm_data') + +# if not exists make path +if not os.access(logpath, os.F_OK): + os.mkdir(logpath) +if not os.access(rtsmpath, os.F_OK): + os.mkdir(rtsmpath) + +hostname = gethostname().split('.')[0].upper() + +# first start main logging before including checkhardware_lib import logging -mainPath = r'/opt/stationtest' -mainDataPath = r'/localhome/stationtest' -observationsPath = r'/opt/lofar/var/run' +# backup log files +for nr in xrange(8, -1, -1): + if nr == 0: + full_filename = os.path.join(logpath, '%s_rtsm.log' % hostname) + else: + full_filename = os.path.join(logpath, '%s_rtsm.log.%d' % (hostname, nr)) + full_filename_new = os.path.join(logpath, '%s_rtsm.log.%d' % (hostname, (nr + 1))) + if os.path.exists(full_filename): + os.rename(full_filename, full_filename_new) -beamletPath = r'/localhome/data/Beamlets' +# make and setup logger +logger = logging.getLogger('main') +logger.setLevel(logging.DEBUG) +# create file handler +filename = '%s_rtsm.log' % hostname +full_filename = os.path.join(logpath, filename) +file_handler = logging.FileHandler(full_filename, mode='w') +formatter = logging.Formatter('%(asctime)s %(name)-14s %(levelname)-8s %(message)s') +file_handler.setFormatter(formatter) +file_handler.setLevel(logging.DEBUG) +logger.addHandler(file_handler) +time.sleep(2.0) +logger.debug("logger is working") -libPath = os.path.join(mainPath, 'lib') -sys.path.insert(0, libPath) +# now include checkhardware library +from checkhardware_lib import * +from checkhardware_lib.spectrum_checks import * +from checkhardware_lib.data import AntennaData -confPath = os.path.join(mainDataPath, 'config') -logPath = os.path.join(mainDataPath, 'log') -rtsmPath = os.path.join(mainDataPath, 'rtsm_data') -from general_lib import * -from lofar_lib import * -from search_lib import * -from data_lib import * +def lba_mode(mode): + return mode in (1, 2, 3, 4) -os.umask(001) -os.nice(15) -# make path if not exists -if not os.access(logPath, os.F_OK): - os.mkdir(logPath) -if not os.access(rtsmPath, os.F_OK): - os.mkdir(rtsmPath) - -logger = None - -def lbaMode(mode): - if mode in (1, 2, 3, 4): - return (True) - return (False) - -def lbaLowMode(mode): - if mode in (1, 2): - return (True) - return (False) - -def lbaHighMode(mode): - if mode in (3, 4): - return (True) - return (False) - -def hbaMode(mode): - if mode in (5, 6, 7): - return (True) - return (False) - -def checkStr(key): - checks = dict({'OSC':"Oscillation", 'HN':"High-noise", 'LN':"Low-noise", 'J':"Jitter", 'SN':"Summator-noise",\ - 'CR':"Cable-reflection", 'M':"Modem-failure", 'DOWN':"Antenna-fallen", 'SHIFT':"Shifted-band"}) - return (checks.get(key, 'Unknown')) - -def printHelp(): - print "----------------------------------------------------------------------------" - print "Usage of arguments" - print - print "Set logging level, can be: debug|info|warning|error" - print "-ls=debug : print all information on screen, default=info" - print "-lf=info : print debug|warning|error information to log file, default=debug" - print - - print "----------------------------------------------------------------------------" - - -def getArguments(): - args = dict() - key = '' - value = '-' - for arg in sys.argv[1:]: - if arg[0] == '-': - opt = arg[1:].upper() - valpos = opt.find('=') - if valpos != -1: - key, value = opt.strip().split('=') - else: - key, value = opt, '-' +def lba_low_mode(mode): + return mode in (1, 2) - if key in ('H','LS','LF'): - if value != '-': - args[key] = value - else: - args[key] = '-' - else: - sys.exit("Unknown key %s" %(key)) - return (args) + +def lba_high_mode(mode): + return mode in (3, 4) + + +def hba_mode(mode): + return mode in (5, 6, 7) + + +def abbr_to_str(key): + checks = {'OSC': "Oscillation", 'HN': "High-noise", 'LN': "Low-noise", 'J': "Jitter", 'SN': "Summator-noise", + 'CR': "Cable-reflection", 'M': "Modem-failure", 'DOWN': "Antenna-fallen", 'SHIFT': "Shifted-band"} + return checks.get(key, 'Unknown') # get and unpack configuration file -class cConfiguration: +class Configuration: def __init__(self): self.conf = dict() - full_filename = os.path.join(confPath, 'checkHardware.conf') + full_filename = os.path.join(confpath, 'checkHardware.conf') f = open(full_filename, 'r') data = f.readlines() f.close() for line in data: - if line[0] in ('#','\n',' '): + if line[0] in ('#', '\n', ' '): continue if line.find('#') > 0: line = line[:line.find('#')] try: key, value = line.strip().split('=') - key = key.replace('_','-') + key = key.replace('_', '-') self.conf[key] = value + except ValueError: + print "Not a valid key, value pair: %s" % line except: - print "Not a valid configuration setting: %s" %(line) - - def getInt(self,key, default=0): - return (int(self.conf.get(key, str(default)))) - - def getFloat(self,key, default=0.0): - return (float(self.conf.get(key, str(default)))) - - def getStr(self,key): - return (self.conf.get(key, '')) - - -# setup default python logging system -# logstream for screen output -# filestream for program log file -def init_logging(args): - log_levels = {'DEBUG' : logging.DEBUG, - 'INFO' : logging.INFO, - 'WARNING': logging.WARNING, - 'ERROR' : logging.ERROR} - - try: - screen_log_level = args.get('LS', 'INFO') - file_log_level = args.get('LF', 'DEBUG') - except: - print "Not a legal log level, try again" - sys.exit(-1) - - station = getHostName() - - # create logger - _logger = logging.getLogger() - _logger.setLevel(logging.DEBUG) - - # create file handler - filename = '%s_rtsm.log' %(getHostName()) - full_filename = os.path.join(logPath, filename) - file_handler = logging.FileHandler(full_filename, mode='w') - formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') - file_handler.setFormatter(formatter) - file_handler.setLevel(log_levels[file_log_level]) - _logger.addHandler(file_handler) - - if (len(_logger.handlers) == 1) and ('LS' in args): - # create console handler - stream_handler = logging.StreamHandler() - fmt = '%s %%(levelname)-8s %%(message)s' %(station) - formatter = logging.Formatter(fmt) - stream_handler.setFormatter(formatter) - stream_handler.setLevel(log_levels[screen_log_level]) - _logger.addHandler(stream_handler) - return (_logger) - -def getRcuMode(n_rcus): -# RCU[ 0].control=0x10337a9c => ON, mode:3, delay=28, att=06 - rcumode = -1 - rcu_info = {} - answer = rspctl("--rcu") - if answer.count('mode:') == n_rcus: - for line in answer.splitlines(): - if line.find('mode:') == -1: - continue - rcu = line[line.find('[')+1 : line.find(']')].strip() - state = line[line.find('=>')+2 : line.find(',')].strip() - mode = line[line.find('mode:')+5] - if rcu.isdigit() and state in ("OFF", "ON") and mode.isdigit(): - rcu_info[int(rcu)] = (state, int(mode)) - - for mode in range(8): - mode_cnt = answer.count("mode:%d" %(mode)) - if mode == 0: - if mode_cnt == n_rcus: - logger.debug("Not observing") - elif mode_cnt > (n_rcus / 3) and answer.count("mode:0") == (n_rcus - mode_cnt): - logger.debug("Now observing in rcumode %d" %(mode)) - rcumode = mode - return (rcumode, rcu_info) - -def getAntPol(rcumode, rcu): - pol_str = ('X','Y') - ant = rcu / 2 - if rcumode == 1: - pol_str = ('Y','X') - ant += 48 - pol = pol_str[rcu % 2] - return (ant, pol) + raise + + def get_int(self, key, default=0): + return int(self.conf.get(key, str(default))) + + def get_float(self, key, default=0.0): + return float(self.conf.get(key, str(default))) + + def get_str(self, key): + return self.conf.get(key, '') + class CSV: - station = "" - obs_id = "" - filename = "" - rcu_mode = 0 - record_timestamp = 0 - @staticmethod - def setObsID(obs_id): - CSV.station = getHostName() - CSV.obs_id = obs_id - CSV.filename = "%s_%s_open.dat" %(CSV.station, CSV.obs_id) - CSV.rcu_mode = 0 - CSV.rec_timestamp = 0 - CSV.writeHeader() - return - @staticmethod - def setRcuMode(rcumode): - CSV.rcu_mode = rcumode + def __init__(self, obsid): + self.obsid = obsid + self.station = hostname + self.filename = "%s_%s_open.dat" %(self.station, self.obsid) + self.rcumode = 0 + self.record_timestamp = 0 + self.write_header() + + def set_rcu_mode(self, rcumode): + self.rcumode = rcumode return - @staticmethod - def setRecordTimestamp(timestamp): - CSV.record_timestamp = timestamp + + def set_record_timestamp(self, timestamp): + self.record_timestamp = timestamp return - @staticmethod - def writeHeader(): - full_filename = os.path.join(rtsmPath, CSV.filename) + + def write_header(self): + full_filename = os.path.join(rtsmpath, self.filename) # write only if new file if not os.path.exists(full_filename): f = open(full_filename, 'w') @@ -239,37 +146,38 @@ class CSV: f.flush() f.close() return - @staticmethod - def writeSpectra(data, rcu, check): - #dumpTime = time.gmtime(CSV.record_timestamp) - #date_str = time.strftime("%Y%m%d", dumpTime) - full_filename = os.path.join(rtsmPath, CSV.filename) + def write_spectra(self, data, rcu, check): + # dumpTime = time.gmtime(self.record_timestamp) + # date_str = time.strftime("%Y%m%d", dumpTime) - logger.debug("start dumping data to %s" %(full_filename)) + full_filename = os.path.join(rtsmpath, self.filename) + + logger.debug("start dumping data to %s" % full_filename) f = open(full_filename, 'a') - if CSV.rcu_mode in (1, 2, 3, 4): - freq = (0 , 100) - elif CSV.rcu_mode in (5,): + freq = (0, 0) + if self.rcumode in (1, 2, 3, 4): + freq = (0 , 100) + elif self.rcumode in (5,): freq = (100, 200) - elif CSV.rcu_mode in (6,): + elif self.rcumode in (6,): freq = (160, 240) - elif CSV.rcu_mode in (7,): + elif self.rcumode in (7,): freq = (200, 300) - spectra_info = "SPECTRA-INFO=%d,%d,%s,%s,%d,%d,%f\n" %\ - (rcu, CSV.rcu_mode, CSV.obs_id, check, freq[0], freq[1], CSV.record_timestamp) - + spectra_info = "SPECTRA-INFO=%d,%d,%s,%s,%d,%d,%f\n" % ( + rcu, self.rcumode, self.obsid, check, freq[0], freq[1], self.record_timestamp) mean_spectra = "MEAN-SPECTRA=[" - for i in np.nan_to_num(data.getMeanSpectra(rcu%2)): - mean_spectra += "%3.1f " %(i) + for i in data.median_all_spectras(freq_band=mode_to_band(self.rcumode), polarity=data.polarity(rcu), masked=True): + mean_spectra += "%3.1f " % np.nan_to_num(i) mean_spectra += "]\n" bad_spectra = "BAD-SPECTRA=[" - for i in np.nan_to_num(data.getSpectra(rcu)): - bad_spectra += "%3.1f " %(i) + for i in data.rcu_mean_spectra(rcu=rcu, masked=True): + #logger.debug("BAD-SPECTRA=%s" % str(i)) + bad_spectra += "%3.1f " % np.nan_to_num(i) bad_spectra += "]\n\n" f.write(spectra_info) @@ -278,222 +186,282 @@ class CSV: f.close() return - @staticmethod - def writeInfo(start_time, stop_time, obsid_samples): - full_filename = os.path.join(rtsmPath, CSV.filename) - logger.debug("add obs_info to %s" %(full_filename)) + + def write_info(self, start_time, stop_time, obsid_samples): + full_filename = os.path.join(rtsmpath, self.filename) + logger.debug("add obs_info to %s" % full_filename) f = open(full_filename, 'a') - f.write('# OBS-ID-INFO=obs_id,start_time,stop_time,obsid_samples\n') - f.write('OBS-ID-INFO=%s,%5.3f,%5.3f,%d\n\n' %(CSV.obs_id, start_time, stop_time, obsid_samples)) + f.write('# OBS-ID-INFO=obsid,start_time,stop_time,obsid_samples\n') + f.write('OBS-ID-INFO=%s,%5.3f,%5.3f,%d\n\n' % (self.obsid, start_time, stop_time, obsid_samples)) f.flush() f.close() return - @staticmethod - def closeFile(): - full_filename = os.path.join(rtsmPath, CSV.filename) - filename_new = CSV.filename.replace('open','closed') - full_filename_new = os.path.join(rtsmPath, filename_new) - logger.debug("rename file from %s to %s" %(full_filename, full_filename_new)) + + def close_file(self): + full_filename = os.path.join(rtsmpath, self.filename) + filename_new = self.filename.replace('open', 'closed') + full_filename_new = os.path.join(rtsmpath, filename_new) + logger.debug("rename file from %s to %s" % (full_filename, full_filename_new)) os.rename(full_filename, full_filename_new) - CSV.obs_id = "" - CSV.filename = "" + self.obsid = "" + self.filename = "" return -def checkForOscillation(data, rcumode, error_list, delta): + +def check_oscillation(data, band, error_list, settings, cvs): logger.debug("start oscillation check") - for pol_nr, pol in enumerate(('X', 'Y')): - #test_data = data.getAll()[:,:1,:] - result = search_oscillation(data, pol, delta) + for pol in ('X', 'Y'): + # test_data = data.getAll()[:,:1,:] + result = check_for_oscillation(data, band, pol, settings) if len(result) > 1: # get mean values from all rcu's (rcu = -1) - bin_nr, ref_max_sum, ref_n_peaks, ref_rcu_low = result[0] + rcu, ref_max_sum, ref_n_peaks, ref_rcu_low = result[0] - #rcu, max_sum, n_peaks, rcu_low = sorted(result[1:], reverse=True)[0] + # rcu, max_sum, n_peaks, rcu_low = sorted(result[1:], reverse=True)[0] if len(result) == 2: - bin_nr, max_sum, n_peaks, rcu_low = result[1] + rcu, max_sum, n_peaks, rcu_low = result[1] else: ref_low = result[0][3] max_low_rcu = (-1, -1) max_sum_rcu = (-1, -1) for i in result[1:]: - bin_nr, max_sum, n_peaks, rcu_low = i - if max_sum > max_sum_rcu[0]: max_sum_rcu = (max_sum, bin_nr) - if (rcu_low - ref_low) > max_low_rcu[0]: max_low_rcu = (rcu_low, bin_nr) + rcu, max_sum, n_peaks, rcu_low = i + if max_sum > max_sum_rcu[0]: + max_sum_rcu = (max_sum, rcu) + if (rcu_low - ref_low) > max_low_rcu[0]: + max_low_rcu = (rcu_low, rcu) - rcu_low, bin_nr = max_low_rcu + rcu_low, rcu = max_low_rcu - rcu = (bin_nr * 2) + pol_nr - ant, pol = getAntPol(rcumode, rcu) + ant = data.antenna(rcu) + mode = data.mode(rcu) - if lbaMode(rcumode): - logger.info("Mode-%d RCU-%03d Ant-%03d %c Oscillation, sum=%3.1f(%3.1f) peaks=%d(%d) low=%3.1fdB(%3.1f) (=ref)" %\ - (rcumode, rcu, ant, pol, max_sum, ref_max_sum, n_peaks, ref_n_peaks, rcu_low, ref_rcu_low)) + if lba_mode(mode): + logger.info("Mode-%d RCU-%03d Ant-%03d %c " + "Oscillation, sum=%3.1f(%3.1f) peaks=%d(%d) low=%3.1fdB(%3.1f) (=ref)" % ( + mode, rcu, ant, pol, max_sum, ref_max_sum, n_peaks, ref_n_peaks, rcu_low, ref_rcu_low)) if rcu not in error_list: error_list.append(rcu) - CSV.writeSpectra(data, rcu, "OSC") - - if hbaMode(rcumode): - if ((max_sum > 5000.0) or (n_peaks > 40)): - logger.info("Mode-%d RCU-%03d Tile-%02d %c Oscillation, sum=%3.1f(%3.1f) peaks=%d(%d) low=%3.1fdB(%3.1f) ref=()" %\ - (rcumode, rcu, ant, pol, max_sum, ref_max_sum, n_peaks, ref_n_peaks, rcu_low, ref_rcu_low)) + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "OSC") + + if hba_mode(mode): + if max_sum > 5000.0 or n_peaks > 40: + logger.info("Mode-%d RCU-%03d Tile-%02d %c " + "Oscillation, sum=%3.1f(%3.1f) peaks=%d(%d) low=%3.1fdB(%3.1f) ref=()" % ( + mode, rcu, ant, pol, max_sum, ref_max_sum, n_peaks, ref_n_peaks, rcu_low, ref_rcu_low)) if rcu not in error_list: error_list.append(rcu) - CSV.writeSpectra(data, rcu, "OSC") + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "OSC") return -def checkForNoise(data, rcumode, error_list, low_deviation, high_deviation, max_diff): + +def check_noise(data, band, error_list, settings, cvs): logger.debug("start noise check") - for pol_nr, pol in enumerate(('X', 'Y')): - low_noise, high_noise, jitter = search_noise(data, pol, low_deviation, high_deviation*1.5, max_diff) + for pol in ('X', 'Y'): + low_noise, high_noise, jitter = check_for_noise(data, band, pol, settings) for err in high_noise: - bin_nr, val, bad_secs, ref, diff = err - rcu = (bin_nr * 2) + pol_nr - ant, pol = getAntPol(rcumode, rcu) - if lbaMode(rcumode): - logger.info("Mode-%d RCU-%03d Ant-%03d %c High-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" %\ - (rcumode, rcu, ant, pol, val, bad_secs, data.frames, ref, diff)) - if rcu not in error_list: - error_list.append(rcu) - CSV.writeSpectra(data, rcu, "HN") + rcu, val, bad_secs, ref, diff = err - if hbaMode(rcumode): - logger.info("Mode-%d RCU-%03d Tile-%02d %c High-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" %\ - (rcumode, rcu, ant, pol, val, bad_secs, data.frames, ref, diff)) - if rcu not in error_list: - error_list.append(rcu) - CSV.writeSpectra(data, rcu, "HN") + ant = data.antenna(rcu) + mode = data.mode(rcu) + + if lba_mode(mode): + logger.info("Mode-%d RCU-%03d Ant-%03d %c " + "High-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" % ( + mode, rcu, ant, pol, val, bad_secs, data.seconds(), ref, diff)) + + if hba_mode(mode): + logger.info("Mode-%d RCU-%03d Tile-%02d %c " + "High-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" % ( + mode, rcu, ant, pol, val, bad_secs, data.seconds(), ref, diff)) + + if rcu not in error_list: + error_list.append(rcu) + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "HN") for err in low_noise: - bin_nr, val, bad_secs, ref, diff = err - rcu = (bin_nr * 2) + pol_nr - ant, pol = getAntPol(rcumode, rcu) - if lbaMode(rcumode): - logger.info("Mode-%d RCU-%03d Ant-%03d %c Low-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" %\ - (rcumode, rcu, ant, pol, val, bad_secs, data.frames, ref, diff)) - if rcu not in error_list: - error_list.append(rcu) - CSV.writeSpectra(data, rcu, "LN") + rcu, val, bad_secs, ref, diff = err - if hbaMode(rcumode): - logger.info("Mode-%d RCU-%03d Tile-%02d %c Low-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" %\ - (rcumode, rcu, ant, pol, val, bad_secs, data.frames, ref, diff)) - if rcu not in error_list: - error_list.append(rcu) - CSV.writeSpectra(data, rcu, "LN") + ant = data.antenna(rcu) + mode = data.mode(rcu) + + if lba_mode(mode): + logger.info("Mode-%d RCU-%03d Ant-%03d %c " + "Low-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" % ( + mode, rcu, ant, pol, val, bad_secs, data.seconds(), ref, diff)) + + if hba_mode(mode): + logger.info("Mode-%d RCU-%03d Tile-%02d %c " + "Low-noise, value=%3.1fdB bad=%d(%d) limit=%3.1fdB diff=%3.1fdB" % ( + mode, rcu, ant, pol, val, bad_secs, data.seconds(), ref, diff)) + + if rcu not in error_list: + error_list.append(rcu) + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "LN") return -def checkForSummatorNoise(data, rcumode, error_list): + +def check_summator_noise(data, band, error_list, settings, cvs): logger.debug("start summator-noise check") - for pol_nr, pol in enumerate(('X', 'Y')): + for pol in ('X', 'Y'): # sn=SummatorNoise cr=CableReflections - sn, cr = search_summator_noise(data=data, pol=pol, min_peak=2.0) + sn = check_for_summator_noise(data=data, band=band, pol=pol, parset=settings) + cr = check_for_cable_reflection(data=data, band=band, pol=pol, parset=settings) for msg in sn: - bin_nr, peaks, max_peaks = msg - rcu = (bin_nr * 2) + pol_nr - tile, pol = getAntPol(rcumode, rcu) - logger.info("Mode-%d RCU-%03d Tile-%02d %c Summator-noise, cnt=%d peaks=%d" %\ - (rcumode, rcu, tile, pol, peaks, max_peaks)) + rcu, peaks, max_peaks = msg + + tile = data.antenna(rcu) + mode = data.mode(rcu) + + logger.info("Mode-%d RCU-%03d Tile-%02d %c " + "Summator-noise, cnt=%d peaks=%d" % ( + mode, rcu, tile, pol, peaks, max_peaks)) + if rcu not in error_list: error_list.append(rcu) - CSV.writeSpectra(data, rcu, "SN") + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "SN") + for msg in cr: - bin_nr, peaks, max_peaks = msg - rcu = (bin_nr * 2) + pol_nr - tile, pol = getAntPol(rcumode, rcu) - logger.info("Mode-%d RCU-%03d Tile-%02d %c Cable-reflections, cnt=%d peaks=%d" %\ - (rcumode, rcu, tile, pol, peaks, max_peaks)) - #if rcu not in error_list: - #error_list.append(rcu) - #CSV.writeSpectra(data, rcu, "CR") + rcu, peaks, max_peaks = msg + + tile = data.antenna(rcu) + mode = data.mode(rcu) + + logger.info("Mode-%d RCU-%03d Tile-%02d %c " + "Cable-reflections, cnt=%d peaks=%d" % ( + mode, rcu, tile, pol, peaks, max_peaks)) + # if rcu not in error_list: + # error_list.append(rcu) + # cvs.set_rcu_mode(mode) + # cvs.write_spectra(data, rcu, "CR") return -def checkForDown(data, rcumode, error_list, subband): + +def check_down(data, band, error_list, settings, cvs): logger.debug("start down check") - down, shifted = searchDown(data, subband) + down, shifted = check_for_down(data, band, settings) + + ref_max_sb = 0 + ref_max_val = 0.0 + ref_max_bw = 0 + ref_band_pwr = 0.0 + for msg in down: - ant, max_x_sb, max_y_sb, mean_max_sb = msg - rcu = ant * 2 - max_x_offset = max_x_sb - mean_max_sb - max_y_offset = max_y_sb - mean_max_sb - ant, pol = getAntPol(rcumode, rcu) - logger.info("Mode-%d RCU-%02d/%02d Ant-%02d Down, x-offset=%d y-offset=%d" %\ - (rcumode, rcu, (rcu+1), ant, max_x_offset, max_y_offset)) + rcu, max_sb, max_val, max_bw, band_pwr = msg + if rcu == 'Xref': + logger.info("down test X rcus's median values: sb=%d, pwr=%3.1fdB, bw=%d, band-pwr=%3.1fdB" % ( + max_sb, max_val, max_bw, band_pwr)) + continue + if rcu == 'Yref': + logger.info("down test Y rcu's median values: sb=%d, pwr=%3.1fdB, bw=%d, band-pwr=%3.1fdB" % ( + max_sb, max_val, max_bw, band_pwr)) + continue + + max_offset = 292 - max_sb + + ant = data.antenna(rcu) + mode = data.mode(rcu) + pol = data.polarity(rcu) + + logger.info("Mode-%d RCU-%02d Ant-%02d %s Down: sb=%d, pwr=%3.1fdB, bw=%d, band-pwr=%3.1fdB" % ( + mode, rcu, ant, pol, max_sb, max_val, max_bw, band_pwr)) + if rcu not in error_list: error_list.append(rcu) - error_list.append(rcu+1) - CSV.writeSpectra(data, rcu, "DOWN") - CSV.writeSpectra(data, rcu+1, "DOWN") + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "DOWN") return -def checkForFlat(data, rcumode, error_list): + +def check_flat(data, band, error_list, settings, cvs): logger.debug("start flat check") - flat = searchFlat(data) + flat = check_for_flat(data, band, settings) for msg in flat: rcu, mean_val = msg - ant, pol = getAntPol(rcumode, rcu) - logger.info("Mode-%d RCU-%02d Ant-%02d Flat, value=%5.1fdB" %\ - (rcumode, rcu, ant, mean_val)) + + ant = data.antenna(rcu) + mode = data.mode(rcu) + pol = data.polarity(rcu) + + logger.info("Mode-%d RCU-%02d Ant-%02d %s " + "Flat, value=%5.1fdB" % ( + mode, rcu, ant, pol, mean_val)) if rcu not in error_list: error_list.append(rcu) - CSV.writeSpectra(data, rcu, "FLAT") + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "FLAT") return -def checkForShort(data, rcumode, error_list): + +def check_short(data, band, error_list, settings, cvs): logger.debug("start short check") - short = searchShort(data) + short = check_for_short(data, band, settings) for msg in short: rcu, mean_val = msg - ant, pol = getAntPol(rcumode, rcu) - logger.info("Mode-%d RCU-%02d Ant-%02d Short, value=%5.1fdB" %\ - (rcumode, rcu, ant, mean_val)) + + ant = data.antenna(rcu) + mode = data.mode(rcu) + pol = data.polarity(rcu) + + logger.info("Mode-%d RCU-%02d Ant-%02d %s " + "Short, value=%5.1fdB" % ( + mode, rcu, ant, pol, mean_val)) if rcu not in error_list: error_list.append(rcu) - CSV.writeSpectra(data, rcu, "SHORT") + cvs.set_rcu_mode(mode) + cvs.write_spectra(data, rcu, "SHORT") return -def closeAllOpenFiles(): - files = os.listdir(rtsmPath) + +def close_all_open_files(): + files = os.listdir(rtsmpath) for filename in files: if filename.find('open') > -1: - full_filename = os.path.join(rtsmPath, filename) - filename_new = filename.replace('open','closed') - full_filename_new = os.path.join(rtsmPath, filename_new) + full_filename = os.path.join(rtsmpath, filename) + filename_new = filename.replace('open', 'closed') + full_filename_new = os.path.join(rtsmpath, filename_new) os.rename(full_filename, full_filename_new) return -class cDayInfo: + +class DayInfo: def __init__(self): self.date = time.strftime("%Y%m%d", time.gmtime(time.time())) - self.filename = "%s_%s_dayinfo.dat" %(getHostName(), self.date) - self.samples = [0,0,0,0,0,0,0] # RCU-mode 1..7 + self.filename = "%s_%s_dayinfo.dat" % (hostname, self.date) + self.samples = [0, 0, 0, 0, 0, 0, 0] # RCU-mode 1..7 self.obs_info = list() - self.deleteOldDays() - self.readFile() + self.delete_old_days() + self.read_file() - def addSample(self, rcumode=-1): + def add_sample(self, rcumode=-1): date = time.strftime("%Y%m%d", time.gmtime(time.time())) # new day reset data and set new filename if self.date != date: self.date = date self.reset() - if rcumode in range(1,8,1): - self.samples[rcumode-1] += 1 - self.writeFile() + if rcumode in range(1, 8, 1): + self.samples[rcumode - 1] += 1 + self.write_file() - def addObsInfo(self, obs_id, start_time, stop_time, rcu_mode, samples): + def add_obs_info(self, obs_id, start_time, stop_time, rcu_mode, samples): self.obs_info.append([obs_id, start_time, stop_time, rcu_mode, samples]) def reset(self): - self.filename = "%s_%s_dayinfo.dat" %(getHostName(), self.date) - self.samples = [0,0,0,0,0,0,0] # RCU-mode 1..7 + self.filename = "%s_%s_dayinfo.dat" % (hostname, self.date) + self.samples = [0, 0, 0, 0, 0, 0, 0] # RCU-mode 1..7 self.obs_info = list() - self.deleteOldDays() + self.delete_old_days() # after a restart, earlier data is imported - def readFile(self): - full_filename = os.path.join(rtsmPath, self.filename) + def read_file(self): + full_filename = os.path.join(rtsmpath, self.filename) if os.path.exists(full_filename): f = open(full_filename, 'r') lines = f.readlines() @@ -501,319 +469,308 @@ class cDayInfo: for line in lines: if len(line.strip()) == 0 or line.strip()[0] == '#': continue - key,data = line.split('=') + key, data = line.split('=') if key == 'DAY-INFO': self.samples = [int(i) for i in data.split(',')[1:]] if key == 'OBSID-INFO': d = data.split(',') - self.obs_info.append([d[0],float(d[1]),float(d[2]),int(d[3]), int(d[4])]) + self.obs_info.append([d[0], float(d[1]), float(d[2]), int(d[3]), int(d[4])]) # rewrite file every sample - def writeFile(self): - full_filename = os.path.join(rtsmPath, self.filename) + def write_file(self): + full_filename = os.path.join(rtsmpath, self.filename) f = open(full_filename, 'w') f.write('#DAY-INFO date,M1,M2,M3,M4,M5,M6,M7\n') - f.write('DAY-INFO=%s,%d,%d,%d,%d,%d,%d,%d\n' %\ - (self.date, self.samples[0], self.samples[1], self.samples[2], self.samples[3], self.samples[4], self.samples[5], self.samples[6])) + f.write('DAY-INFO=%s,%d,%d,%d,%d,%d,%d,%d\n' % ( + self.date, self.samples[0], self.samples[1], self.samples[2], self.samples[3], + self.samples[4], self.samples[5], self.samples[6])) f.write('\n#OBS-ID-INFO obs_id, start_time, stop_time, rcu_mode, samples\n') for i in self.obs_info: - f.write('OBS-ID-INFO=%s,%5.3f,%5.3f,%d,%d\n' %\ - (i[0],i[1],i[2],i[3],i[4])) + f.write('OBS-ID-INFO=%s,%5.3f,%5.3f,%d,%d\n' % ( + i[0], i[1], i[2], i[3], i[4])) f.close() - def deleteOldDays(self): - files = os.listdir(rtsmPath) + def delete_old_days(self): + files = os.listdir(rtsmpath) backup = True for filename in files: if filename.find('closed') != -1: backup = False - if backup == True: + if backup: for filename in files: if filename.find('dayinfo') != -1: if filename.split('.')[0].split('_')[1] != self.date: - full_filename = os.path.join(rtsmPath, filename) + full_filename = os.path.join(rtsmpath, filename) os.remove(full_filename) - -def getObsId(): - #obs_start_str = "" - #obs_stop_str = "" - #obs_start_time = 0.0 - #obs_stop_time = 0.0 - obsids = "" - answer = sendCmd('swlevel') + +def get_obs_id(): + """ check swlevel for active obsid, + return: list with active obsids + """ + obsids = [] + answer = run_cmd('swlevel') if answer.find("ObsID") > -1: - s1 = answer.find("ObsID:")+6 + s1 = answer.find("ObsID:") + 6 s2 = answer.find("]") obsids = answer[s1:s2].strip().split() - return (obsids) + return obsids + - -def getObsIdInfo(obsid): - filename = "Observation%s" %(obsid.strip()) - fullfilename = os.path.join(observationsPath, filename) +def get_obs_id_info(obsid): + filename = "Observation%s" % obsid.strip() + fullfilename = os.path.join(observationspath, filename) f = open(fullfilename, 'r') obsinfo = f.read() f.close() + + m1 = obsinfo.find("Observation.receiverList") + m2 = obsinfo.find("\n", m1) + obs_rcu_str = obsinfo[m1:m2].split("=")[1].strip() + #print obs_rcu_str + obs_rcu_list = extract_select_str(obs_rcu_str[1:-1]) + m1 = obsinfo.find("Observation.startTime") m2 = obsinfo.find("\n", m1) - obs_start_str = obsinfo[m1:m2].split("=")[1].strip() + obs_start_str = obsinfo[m1:m2].split("=")[1].strip() + #print obs_start_str obs_start_time = time.mktime(time.strptime(obs_start_str, "%Y-%m-%d %H:%M:%S")) - - m1 = obsinfo.find("Observation.stopTime",m2) + + m1 = obsinfo.find("Observation.stopTime") m2 = obsinfo.find("\n", m1) - obs_stop_str = obsinfo[m1:m2].split("=")[1].strip() + obs_stop_str = obsinfo[m1:m2].split("=")[1].strip() + #print obs_stop_str obs_stop_time = time.mktime(time.strptime(obs_stop_str, "%Y-%m-%d %H:%M:%S")) - - logger.debug("obsid %s %s .. %s" %(obsid, obs_start_str, obs_stop_str)) - return(obsid, obs_start_time, obs_stop_time) + + logger.debug("obsid=%s time=%s..%s rcus=%s" % (obsid, obs_start_str, obs_stop_str, obs_rcu_str)) + #print obs_start_time, obs_stop_time, time.time() + return obsid, obs_start_time, obs_stop_time, obs_rcu_list + class RecordBeamletStatistics(Thread): - def __init__(self): + def __init__(self, obsid, starttime, duration): Thread.__init__(self) self.running = False - self.reset() - - def reset(self): - self.dump_dir = '' - self.obsid = '' - self.duration = 0 - - def set_obsid(self, obsid): - self.dump_dir = os.path.join(beamletPath, obsid) + self.obsid = obsid + self.starttime = starttime + self.duration = duration + self.dump_dir = os.path.join(BEAMLETPATH, self.obsid) try: os.mkdir(self.dump_dir) + self.ready = True + except OSError: + self.ready = False except: - pass - self.obsid = obsid - - def set_duration(self, duration): - self.duration = duration - + raise + def is_running(self): return self.running - + def kill_recording(self): if self.running: logger.debug("kill recording beamlet statistics") - sendCmd(cmd='pkill', args='rspctl') + run_cmd(cmd='killall rspctl') logger.debug("recording killed") - #self.running = False - #self.make_plots() - - def make_plots(self): - if self.obsid: - try: - response = sendCmd(cmd='/home/fallows/inspect_bsts.bash', args=self.obsid) - logger.debug('response "inspect.bsts.bash" = {%s}' % response) - except: - logger.debug('exception while running "inspect.bsts.bash"') - self.reset() - + def run(self): + sleeptime = self.starttime - time.time() + if sleeptime > 0.0: + time.sleep(sleeptime) if self.duration: self.running = True logger.debug("start recording beamlet statistics for %d seconds" % self.duration) rspctl('--statistics=beamlet --duration=%d --integration=1 --directory=%s' % (self.duration, self.dump_dir)) logger.debug("recording done") - self.make_plots() self.running = False - + def main(): - global logger - obs_id = "" - active_obs_id = "" - rcumode = 0 - #station = getHostName() - DI = cDayInfo() - - args = getArguments() - if args.has_key('H'): - printHelp() - sys.exit() - - logger = init_logging(args) - init_lofar_lib() - init_data_lib() + obs_id = "" + active_obs_id = "" + rcumode = 0 + dayinfo = DayInfo() - conf = cConfiguration() + init_lofar_lib() - #StID = getHostName() + # get test settings from configuration file + conf = TestSettings(filename=conf_file) logger.info('== Start rtsm (Real Time Station Monitor) ==') - removeAllDataFiles() - # Read in RemoteStation.conf - ID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT = readStationConfig() + st_id, n_rsp, n_tbb, n_lbl, n_lbh, n_hba, hba_split = read_station_config() - n_rcus = nRSP * 8 + n_rcus = n_rsp * 8 - data = cRCUdata(n_rcus) + data = AntennaData({'n_rcus': n_rcus, 'data-dir': data_dir()}) - obs_start_time = 0 - obs_stop_time = 0 - obsid_samples = 0 - - beamlet_recording = RecordBeamletStatistics() - - + obs_info = {} + obs_info_to_delete = [] + + close_all_open_files() + + sleep_counter = 0 while True: try: - # get active obsid from swlevel - obsids = getObsId() - time_now = time.time() - # stop if no more obsids or observation is stoped - if obs_stop_time > 0.0: - if active_obs_id not in obsids or len(obsids) == 0 or time_now > obs_stop_time: - logger.debug("save obs_id %s" %(obs_id)) - DI.addObsInfo(obs_id, obs_start_time, obs_stop_time, rcumode, obsid_samples) - DI.writeFile() - CSV.writeInfo(obs_start_time, obs_stop_time, obsid_samples) - CSV.closeFile() - active_obs_id = "" - obs_start_time = 0.0 - obs_stop_time = 0.0 - # if still running kill recording - if beamlet_recording: - if beamlet_recording.is_running(): - beamlet_recording.kill_recording() - beamlet_recording = 0 - - # if no active observation get obs info if obsid available - if active_obs_id == "": - # if still running kill recording - if beamlet_recording: - if beamlet_recording.is_running(): - beamlet_recording.kill_recording() - beamlet_recording = 0 - - for id in obsids: - obsid, start, stop = getObsIdInfo(id) - if time_now >= (start - 60.0) and (time_now + 15) < stop: - active_obs_id = obsid - obs_start_time = start - obs_stop_time = stop - break - - if time_now < obs_start_time: - logger.debug("waiting %d seconds for start of observation" %(int(obs_start_time - time_now))) - time.sleep((obs_start_time - time_now) + 1.0) - - # start recording beamlets - if not beamlet_recording: - if obs_start_time > 0.0 and time.time() >= obs_start_time: - duration = obs_stop_time - time.time() - 10 - if duration > 2: - beamlet_recording = RecordBeamletStatistics() - beamlet_recording.set_obsid(active_obs_id) - beamlet_recording.set_duration(duration) - beamlet_recording.start() - - check_start = time.time() - # if new obs_id save data and reset settings - if obs_id != active_obs_id: - # start new file and set new obsid - obs_id = active_obs_id - obsid_samples = 0 - CSV.setObsID(obs_id) - - # it takes about 11 seconds to record data, for safety use 15 - if (time.time() + 15.0) < obs_stop_time: - # observing, so check mode now - rcumode, rcu_info = getRcuMode(n_rcus) - if rcumode <= 0: - continue - active_rcus = [] - for rcu in rcu_info: - state, mode = rcu_info[rcu] - if state == 'ON': - active_rcus.append(rcu) - data.setActiveRcus(active_rcus) - - rec_timestamp = time.time()+3.0 - data.record(rec_time=1, read=True, slow=True) - #data.fetch() - - CSV.setRcuMode(rcumode) - CSV.setRecordTimestamp(rec_timestamp) - DI.addSample(rcumode) - obsid_samples += 1 - logger.debug("do tests") - mask = extractSelectStr(conf.getStr('mask-rcumode-%d' %(rcumode))) - data.setMask(mask) - if len(mask) > 0: - logger.debug("mask=%s" %(str(mask))) - - error_list = [] - # do LBA tests - if lbaMode(rcumode): - checkForDown(data, rcumode, error_list, - conf.getInt('lbh-test-sb',301)) - checkForShort(data, rcumode, error_list) - checkForFlat(data, rcumode, error_list) - checkForOscillation(data, rcumode, error_list, 6.0) - checkForNoise(data, rcumode, error_list, - conf.getFloat('lba-noise-min-deviation', -3.0), - conf.getFloat('lba-noise-max-deviation', 2.5), - conf.getFloat('lba-noise-max-difference', 1.5)) - # do HBA tests - if hbaMode(rcumode): - checkForOscillation(data, rcumode, error_list, 9.0) - checkForSummatorNoise(data, rcumode, error_list) - checkForNoise(data, rcumode, error_list, - conf.getFloat('hba-noise-min-deviation', -3.0), - conf.getFloat('hba-noise-max-deviation', 2.5), - conf.getFloat('hba-noise-max-difference', 2.0)) - else: - - closeAllOpenFiles() - - if active_obs_id == "": + # get list with active obsids from swlevel, [] if none + obsids = get_obs_id() + + # loop over obsids and start new proces for each obsid, asuming more than one observation can be run + # get also used rcus from parameterset + if obsids: + for _obsid in obsids: + if not _obsid in obs_info: + # new obsid, setup and start recording beamlet statistics + obs_info[_obsid] = {} + obsid, start, stop, rcus = get_obs_id_info(_obsid) + obs_info[_obsid]['starttime'] = start + obs_info[_obsid]['stoptime'] = stop + obs_info[_obsid]['rcus'] = rcus + obs_info[_obsid]['state'] = 'new' + obs_info[_obsid]['csv'] = CSV(_obsid) + if time_now > (start + 60.0): + starttime = time_now + else: + starttime = start + 60.0 + duration = stop - starttime - 10.0 + #print starttime, duration + rbc = RecordBeamletStatistics(_obsid, starttime, duration) + obs_info[_obsid]['beamlet-recording'] = rbc + obs_info[_obsid]['beamlet-recording'].start() + obs_info[_obsid]['next-check-time'] = starttime + obs_info[_obsid]['last-check-time'] = stop - 15.0 + obs_info[_obsid]['samples'] = 0 + #print str(obs_info[_obsid]) + + # close finished obsids + for _obsid in obs_info_to_delete: + if obs_info[_obsid]['state'] == 'finished': + del obs_info[_obsid] + obs_info_to_delete = [] + + # mark stopped obsids as stopped + for _obsid in obs_info.iterkeys(): + if not _obsid in obsids: + obs_info[_obsid]['state'] = 'stopped' + obs_info_to_delete.append(_obsid) + + check_now = False + for _obsid in obs_info.iterkeys(): + if time_now >= obs_info[_obsid]['next-check-time']: + check_now = True + + if check_now: + # observing, so check mode now + rec_timestamp = time.time() + 3.0 + data.collect(n_seconds=1, slow=True) + # data.fetch() + + for _obsid in obs_info.iterkeys(): + # finish stopped obsid, and stop recording if needed + if obs_info[_obsid]['state'] == 'stopped': + # dayinfo.add_obs_info(obs_id, obs_start_time, obs_stop_time, rcumode, obsid_samples) + # dayinfo.write_file() + obs_info[_obsid]['csv'].write_info(obs_info[_obsid]['starttime'], + obs_info[_obsid]['stoptime'], + obs_info[_obsid]['samples']) + obs_info[_obsid]['csv'].close_file() + if obs_info[_obsid]['beamlet-recording'].is_running(): + obs_info[_obsid]['beamlet-recording'].kill_recording() + time.sleep(0.2) + if not obs_info[_obsid]['beamlet-recording'].is_running(): + obs_info[_obsid]['state'] = 'finished' + continue # finished, next obsid + + if time_now > obs_info[_obsid]['last-check-time']: + continue + + if time_now < obs_info[_obsid]['next-check-time']: + continue + + obs_info[_obsid]['csv'].set_record_timestamp(rec_timestamp) + obs_info[_obsid]['samples'] += 1 + logger.debug("do tests") + + error_list = [] + + for band in AntennaData.bands: + if data.band_active(band): + # TODO: DI.add_sample(rcumode) + # mask = extract_select_str(conf.get_str('mask-band-%d' % band)) + # data.mask_sb(mask) + # if len(mask) > 0: + # logger.debug("mask=%s" %(str(mask))) + data.reset_masked_rcus() + masked_rcus = [] + for i in xrange(n_rcus): + if not i in obs_info[_obsid]['rcus']: + masked_rcus.append(i) + data.mask_rcu(masked_rcus) + # check rcumode of first rcu + mode = data.mode(obs_info[_obsid]['rcus'][0]) + # do LBA tests + logger.debug("band= %s, mode= %d" % (band, mode)) + if mode in (1, 2, 3, 4) and band in ('10_90', '30_90'): + settings = conf.rcumode(mode) + check_down(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_short(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_flat(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_oscillation(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_noise(data, band, error_list, settings, obs_info[_obsid]['csv']) + + # do HBA tests + if mode in (5, 6, 7) and band in ('110_190', '170_210', '210_250'): + settings = conf.group('rcumode.%d.tile' % mode) + check_oscillation(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_summator_noise(data, band, error_list, settings, obs_info[_obsid]['csv']) + check_noise(data, band, error_list, settings, obs_info[_obsid]['csv']) + + next_check_time = obs_info[_obsid]['next-check-time'] + obs_info[_obsid]['next-check-time'] = next_check_time + 60.0 + logger.debug("next check obsid %s on %s" %(_obsid, time.ctime(next_check_time + 60.0))) + #print str(obs_info[_obsid]) + + if len(obs_info) == 0: # if not observing check every 30 seconds for observation start - sleeptime = 30.0 - logger.debug("no observation, sleep %1.0f seconds" %(sleeptime)) + if sleep_counter == 0: + logger.debug("no observation, sleep mode activated") + sleep_counter += 1 + if (sleep_counter % 60) == 0: + logger.debug("no observation, still sleeping") + time.sleep(10.0) else: - # if observing do check every 60 seconds - check_stop = time.time() - sleeptime = 60.0 - (check_stop - check_start) - logger.debug("sleep %1.0f seconds till next check" %(sleeptime)) - while sleeptime > 0.0: - wait = min(1.0, sleeptime) - sleeptime -= wait - time.sleep(wait) + # if observing do check every 1 seconds + sleep_counter = 0 + time.sleep(1.0) except KeyboardInterrupt: logger.info("stopped by user") - sys.exit() + return 0 except: logger.error('Caught %s', str(sys.exc_info()[0])) logger.error(str(sys.exc_info()[1])) logger.error('TRACEBACK:\n%s', traceback.format_exc()) logger.error('Aborting NOW') - sys.exit(0) - + return 1 # do test and write result files to log directory - log_dir = conf.getStr('log-dir-local') + log_dir = conf.get_str('log-dir-local') if os.path.exists(log_dir): logger.info("write result data") # write result else: - logger.warn("not a valid log directory") + logger.warning("not a valid log directory") logger.info("Test ready.") # if still running kill recording if beamlet_recording: if beamlet_recording.is_running(): beamlet_recording.kill_recording() - beamlet_recording = 0 # delete files from data directory - removeAllDataFiles() - sys.exit(0) + remove_all_data_files() + return 0 if __name__ == '__main__': - main() + sys.exit(main()) diff --git a/LCU/checkhardware/showBadSpectra.py b/LCU/checkhardware/show_bad_spectra.py similarity index 92% rename from LCU/checkhardware/showBadSpectra.py rename to LCU/checkhardware/show_bad_spectra.py index 3a98ba22f7b080a943e217d15fd5641f52c9b458..63e990dcbb386d8b82882db2d24fbf71d9e33e6e 100755 --- a/LCU/checkhardware/showBadSpectra.py +++ b/LCU/checkhardware/show_bad_spectra.py @@ -5,10 +5,11 @@ import numpy as np import matplotlib.pyplot as plt import time -obs_id_to_plot = '' +obs_id_to_plot = '' station_to_plot = '' -spectraPath = r'/localhome/stationtest/bad_spectra' +spectraPath = r'/localhome/stationtest/bad_spectra' + def main(): files = full_listdir(spectraPath) @@ -41,13 +42,13 @@ def main(): rcu = val_data if val_name == 'frequency': freq = str2array(val_data[1:-1]) - #print len(freq) + # print len(freq) if val_name == 'mean-spectra': mean_spectra = str2array(val_data[1:-1]) - #print len(mean_spectra) + # print len(mean_spectra) if val_name == 'rcu-spectra': bad_rcu = str2array(val_data[1:-1]) - #print len(bad_rcu) + # print len(bad_rcu) plt.plot(freq,mean_spectra,'k',freq,bad_rcu,'r') plt.legend(('median-spectra', 'rcu-%s' %(rcu)), fancybox=True) diff --git a/LCU/checkhardware/showTestResult.py b/LCU/checkhardware/show_test_result.py similarity index 92% rename from LCU/checkhardware/showTestResult.py rename to LCU/checkhardware/show_test_result.py index 1900b69c9238132f7ef03529e397ebb1bbf50583..6835d1f4230b739bfcca3f6939961cbd21ba96dc 100755 --- a/LCU/checkhardware/showTestResult.py +++ b/LCU/checkhardware/show_test_result.py @@ -8,7 +8,8 @@ import string # import datetime # import time -logdir = '' +report_dir = '' +conf_file = r'/localhome/stationtest/config/check_hardware.conf' def print_help(): """ print help """ @@ -42,38 +43,36 @@ if 'P' in args: lib_path = run_path+r'/lib' sys.path.insert(0, lib_path) -from general_lib import * -from lofar_lib import * +from checkhardware_lib import * -station_id = getHostName().upper() +hostname = get_hostname() +if hostname == 'Unknown': + sys.exit('hostname unknown') +station_id = hostname.upper() def main(): - global logdir - + global report_dir + """ main function """ - fd = open(run_path+r'/checkHardware.conf', 'r') - data = fd.readlines() - fd.close() - for line in data: - if line.find('log-dir-local') != -1: - key, logdir = line.strip().split('=') + conf = TestSettings(filename=conf_file) + report_dir = conf().as_string('paths.local-report-dir') data_sets = [] - + if 'F' in args: fullfilename = args.get('F') else: if 'L2' in args: testfilename = '%s_L2_StationTestHistory.csv' % station_id data_sets.append( ['L2', get_data(testfilename, int(args.get('L2', '1')))] ) - - if 'S' in args: + + if 'S' in args: testfilename = '%s_S_StationTestHistory.csv' % station_id data_sets.append( ['S', get_data(testfilename, int(args.get('S', '1')))] ) - + if not 'L2' in args and not 'S' in args: - testfilename = '%s_StationTest.csv' % station_id + testfilename = '%s_station_test.csv' % station_id data_sets.append( ['', get_data(testfilename, 1)] ) rcu_x = rcu_y = 0 @@ -84,13 +83,13 @@ def main(): # print data for all sets print "\n\n\n" for check_type, data in data_sets: - message = "STATION-CHECK RESULTS %s for last %s checks" % (check_type, args.get('%s' % check_type, '1')) + message = "STATION-CHECK RESULTS %s for last %s checks" % (check_type, args.get('%s' % check_type, '1')) banner_len = 100 msg_len = len(message) print "-" * banner_len print ">" * ((banner_len - msg_len - 6) / 2) + " %s " % message + "<" * ((banner_len - msg_len - 6) / 2) print "-" * banner_len - + check_nr = int(args.get('%s' % check_type, '1')) - 1 for line in data: partnumber = -1 @@ -107,9 +106,9 @@ def main(): message = "= csv -%s- (last - %d) =" % (check_type, check_nr) else: message = "= csv -%s- (last) =" % check_type - print ' ' + '=' * len(message) + print ' ' + '=' * len(message) print ' ' + message - print ' ' + '=' * len(message) + print ' ' + '=' * len(message) check_nr -= 1 part = d[1] @@ -200,8 +199,8 @@ def main(): def get_data(filename, n_checks): if not filename.startswith('/'): - if os.path.exists(logdir): - fullfilename = os.path.join(logdir, filename) + if os.path.exists(report_dir): + fullfilename = os.path.join(report_dir, filename) else: print "not a valid log dir" sys.exit(-1) @@ -210,9 +209,9 @@ def get_data(filename, n_checks): data = fd.readlines() fd.close() except: - print "%s not found in %s" % (filename, logdir) + print "%s not found in %s" % (filename, report_dir) sys.exit(-1) - + first_line = 0 check_cnt = 0 for i in range(len(data) - 1, -1, -1): @@ -255,19 +254,24 @@ def print_info(msg, keyvalue, msg_info): if msg == 'CHECKS': """E5""" checks = msg_info.split() - info = [] + info1 = [] + info2 = [] if 'RV' in checks: - info.append('RSP-version') + info1.append('RSP-firmware-version') if 'TV' in checks: - info.append('TBB-version') + info1.append('TB-firmware-version') if 'TM' in checks: - info.append('TBB-memory') + info1.append('TB-memory-check') if 'SPU' in checks: - info.append('SPU-check') - if 'RBV' in checks: - info.append('RSP-voltage') - if len(info): - print "-- Checks done : %s" % string.join(info, ', ') + info2.append('SPU-board-check') + if 'RBC' in checks: + info2.append('RSP-board-checks') + if 'TBC' in checks: + info2.append('TB-board-checks') + if len(info1) or len(info2): + print "-- Checks done : %s" % string.join(info1, ', ') + if len(info2): + print " : %s" % string.join(info2, ', ') info = [] for mode in '1234567': if 'M%s' % mode in checks: @@ -279,9 +283,9 @@ def print_info(msg, keyvalue, msg_info): if 'D%s' % mode in checks: info.append('Down') if 'S%s' % mode in checks: - info.append('RF') + info.append('RF') if 'O%s' % mode in checks: - info.append('Oscillation') + info.append('Oscillation') if 'SP%s' % mode in checks: info.append('Spurious') if 'SN%s' % mode in checks: @@ -290,8 +294,8 @@ def print_info(msg, keyvalue, msg_info): if 'NS%s' % mode in check.split('='): info.append('Noise[%ssec]' % check.split('=')[1]) if 'E%s' % mode in checks: - info.append('Elements') - if len(info): + info.append('Elements') + if len(info): print "-- Checks done M%s : %s" % (mode, string.join(info, ', ')) info = [] @@ -362,7 +366,13 @@ def print_tbb(partnumber, msg, keyvalue): if 'TP' in keyvalue or 'MP' in keyvalue: print " Board %2d wrong firmware version: TP=%s MP=%s" % ( partnumber, keyvalue.get('TP'), keyvalue.get('MP')) - + if msg == 'VOLTAGE': + print " Board %2d wrong voltage: 1.2V=%s 2.5V=%s 3.3V=%s" % ( + partnumber, keyvalue.get('1.2V'), keyvalue.get('2.5V'), keyvalue.get('3.3V')) + if msg == 'TEMPERATURE': + print " Board %2d high temperature: PCB=%s TP=%s MP0=%s MP1=%s MP2=%s MP3=%s" % ( + partnumber, keyvalue.get('PCB'), keyvalue.get('TP'), keyvalue.get('MP0'), keyvalue.get('MP1'), + keyvalue.get('MP2'), keyvalue.get('MP3')) if msg == 'MEMORY': print " Board %2d Memory address or dataline error" % partnumber return diff --git a/LCU/checkhardware/updatePVSS.py b/LCU/checkhardware/update_pvss.py similarity index 87% rename from LCU/checkhardware/updatePVSS.py rename to LCU/checkhardware/update_pvss.py index e6db9a742dd11b827169defa3fafb32c5e70e7bc..3a4b4955098bf9819839d398ecf25507a909d498 100755 --- a/LCU/checkhardware/updatePVSS.py +++ b/LCU/checkhardware/update_pvss.py @@ -10,14 +10,12 @@ import sys import os from time import sleep -libPath = '/opt/stationtest/lib' -sys.path.insert(0, libPath) +from checkhardware_lib import * -from general_lib import * -from lofar_lib import * +conf_file = r'/localhome/stationtest/config/check_hardware.conf' args = dict() -logdir = "" +conf = None logger = 0 nLBL = 0 nLBH = 0 @@ -27,34 +25,40 @@ nRSP = 0 # PVSS states State = dict({'OFF':0, 'OPERATIONAL':1, 'MAINTENANCE':2, 'TEST':3, 'SUSPICIOUS':4, 'BROKEN':5}) +hostname = get_hostname().upper() +if hostname == 'Unknown': + sys.exit('hostname unknown') def main(): - global args, logdir, logger, nRSP, nLBL, nLBH, nHBA + global conf, args, logger, nRSP, nLBL, nLBH, nHBA getArguments() - logdir = getLogDir() - ID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT = readStationConfig() - logger = cPVSSLogger(logdir) - + + conf = TestSettings(filename=conf_file) + report_dir = conf().as_string('paths.local-report-dir') + + ID, nRSP, nTBB, nLBL, nLBH, nHBA, HBA_SPLIT = read_station_config() + logger = MyPVSSLogger(report_dir, hostname) + if args.has_key('RESET'): resetPVSS(state=0) - + if args.has_key('NO_UPDATE'): - print "skip PVSS update" - + print "skip PVSS update" + addManualDataToPVSS() - + # read last log file from checkhardware - testfilename = '%s_StationTest.csv' %(getHostName()) - fullFilename = os.path.join(logdir, testfilename) + testfilename = '%s_station_test.csv' % hostname + fullFilename = os.path.join(report_dir, testfilename) if args.has_key('FILE'): fullFilename = args.get('FILE') - + try: f = open(fullFilename, 'r') except IOError: print "file not found %s" %(fullFilename) return - + testdata = f.readlines() f.close() bad_lba, bad_hba = addDataToPVSS(testdata) @@ -66,7 +70,7 @@ def printHelp(): print "Usage of arguments" print "Output of last stationcheck is always send to pvss also the bad_rcu file is made" print "-h : this help screen" - + print "-reset[=type] : set all state fields to ok for type if given" print " type = all | lba | lbl | lbh | hba (all=default)" print "-no_update : skip pvss update" @@ -96,7 +100,7 @@ def printHelp(): print " or available more than y% of time and fluctuation > z dB" print "-LBHJ=x,y,z : jitter, flag only if available more than x% of time (x=0..100)" print " or available more than y% of time and fluctuation > z dB" - + # get command line arguments def getArguments(): global args @@ -104,7 +108,7 @@ def getArguments(): if sys.argv[i][0] == '-': if sys.argv[i].find('=') != -1: valpos = sys.argv[i].find('=') - key = sys.argv[i][1:valpos].upper() + key = sys.argv[i][1:valpos].upper() val = sys.argv[i][valpos+1:].split(',') if len(val) > 1: args[key] = val @@ -118,35 +122,25 @@ def getArguments(): sys.exit() return -# get logdir from configuration file -def getLogDir(): - logdir = "" - # look for log directory - f = open("/opt/stationtest/checkHardware.conf", 'r') - data = f.readlines() - f.close() - for line in data: - if line.find('log-dir-local') != -1: - key, logdir = line.strip().split('=') - return(logdir) - # send comment, key and value to PVSS and write to file def sendToPVSS(comment, pvss_key, value): global logger, args if args.has_key('NO_UPDATE'): return("") - + if len(comment) > 0: comment = 'stationtest::'+comment else: comment = 'stationtest' + + # add extra argument to setObjectState force=true to reset failure. arguments = '%s %s %d' %(comment, pvss_key, value) - logger.addLine(arguments[11:]) + logger.add_line(arguments[11:]) if args.has_key('TEST'): print arguments else: - response = sendCmd('setObjectState', arguments) + response = run_cmd('setObjectState %s' % arguments) sleep(0.2) return(response) return("") @@ -154,19 +148,19 @@ def sendToPVSS(comment, pvss_key, value): # set all antenna info to ok def resetPVSS(state=0): global args, libPath, State, nRSP, nLBL, nLBH, nHBA - + reset_type = args.get('RESET','ALL').upper() filename = "reset_pvss.log" full_filename = os.path.join(libPath, filename) f = open(full_filename, 'w') - - if reset_type == 'ALL': + + if reset_type == 'ALL': for rcu in range(nRSP*8): board = int(rcu / 8) rack = int(board / 4) - cabinet = int(rack / 2) + cabinet = int(rack / 2) f.write("LOFAR_PIC_Cabinet%d_Subrack%d_RSPBoard%d_RCU%d %d\n" %(cabinet, rack, board, rcu, state)) - + if reset_type in ('ALL','LBA','LBH'): for ant in range(nLBH): f.write("LOFAR_PIC_LBA%03d %d\n" %(ant, state)) @@ -175,10 +169,10 @@ def resetPVSS(state=0): for ant in range(nLBL): f.write("LOFAR_PIC_LBA%03d %d\n" %(ant+48, state)) - if reset_type in ('ALL','HBA'): + if reset_type in ('ALL','HBA'): for tile in range(nHBA): f.write("LOFAR_PIC_HBA%02d %d\n" %(tile, state)) - + for elem in range(16): f.write("LOFAR_PIC_HBA%02d.element%02d %d\n" %(tile, elem, state)) f.write("LOFAR_PIC_HBA%02d.element%02d.comm %d\n" %(tile, elem, state)) @@ -186,16 +180,14 @@ def resetPVSS(state=0): f.write("LOFAR_PIC_HBA%02d.element%02d.Y %d\n" %(tile, elem, state)) f.close() if not args.has_key('TEST'): - sendCmd("setObjectState", "stationtest:reset %s" %(full_filename)) + run_cmd("setObjectState stationtest:reset %s" % full_filename) sleep(5.0) - + # add manual filled list with bad antennas to pvss def addManualDataToPVSS(): - global State, logdir - filename = "bad_antenna_list.txt" - full_filename = "/globalhome/log/bad_antenna_list.txt" + full_filename = conf().get('files.bad-antenna-list') try: f = open(full_filename, 'r') except IOError: @@ -206,7 +198,7 @@ def addManualDataToPVSS(): for line in data: if line[0] == '#': continue - if line.upper().find(getHostName()) > -1: + if line.upper().find(hostname) > -1: bad_antenna_list = line.strip().split(' ')[1:] for ant in bad_antenna_list: part = ant[:3].upper() @@ -216,18 +208,15 @@ def addManualDataToPVSS(): if part == 'HBA': sendToPVSS("manualy-marked", "LOFAR_PIC_HBA%02d" %(part_nr), State['BROKEN']) return - + # add result data from checkhardware to PVSS def addDataToPVSS(data): - global args - global State bad_lba = dict() - bad_hba = dict() - + bad_hba = dict() + RFrefX = 0.0 RFrefY = 0.0 - - + for line in data: if line[0] == '#': continue @@ -250,8 +239,8 @@ def addDataToPVSS(data): keyinfo[key] = vallist else: keyinfo[info[i]] = '-' - - + + if part == 'LBL': lban_limits = args.get('LBLN','0.0') lbaj_limits = args.get('LBLJ','0.0') @@ -260,12 +249,12 @@ def addDataToPVSS(data): lban_limits = args.get('LBHN','0.0') lbaj_limits = args.get('LBHJ','0.0') lbas_limit = args.get('LBHS','0.0') - + if part in ('LBL', 'LBH'): if msgType == 'TESTSIGNAL': RFrefX = float(keyinfo.get('SIGNALX','0.0')) RFrefY = float(keyinfo.get('SIGNALY','0.0')) - + if msgType == 'LOW_NOISE': if float(keyinfo.get('Xproc','0.0')) >= 100.0 or float(keyinfo.get('Yproc','0.0')) >= 100.0: sendToPVSS("low-noise", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) @@ -280,7 +269,7 @@ def addDataToPVSS(data): diff_limit = float(lban_limits[2]) else: proc_limit_1 = float(lban_limits) - + if float(keyinfo.get('Xproc','0.0')) >= proc_limit_1 or float(keyinfo.get('Yproc','0.0')) >= proc_limit_1: if ((float(keyinfo.get('Xproc','0.0')) < proc_limit_2 and (float(keyinfo.get('Xval','0.0')) - float(keyinfo.get('Xref','0.0'))) < diff_limit) and (float(keyinfo.get('Yproc','0.0')) < proc_limit_2 and (float(keyinfo.get('Yval','0.0')) - float(keyinfo.get('Yref','0.0'))) < diff_limit)): @@ -298,7 +287,7 @@ def addDataToPVSS(data): diff_limit = float(lbaj_limits[2]) else: proc_limit_1 = float(lbaj_limits) - + if float(keyinfo.get('Xproc','0.0')) >= proc_limit_1 or float(keyinfo.get('Yproc','0.0')) >= proc_limit_1: if ((float(keyinfo.get('Xproc','0.0')) < proc_limit_2 and float(keyinfo.get('Xdiff','0.0')) < diff_limit) and (float(keyinfo.get('Yproc','0.0')) < proc_limit_2 and float(keyinfo.get('Ydiff','0.0')) < diff_limit)): @@ -306,7 +295,7 @@ def addDataToPVSS(data): else: sendToPVSS("jitter", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) bad_lba[partNr] = 1 - + elif msgType == 'OSCILLATION': sendToPVSS("oscillating", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) bad_lba[partNr] = 1 @@ -323,22 +312,22 @@ def addDataToPVSS(data): if Y > 0.0: if abs(Y - RFrefY) > float(lbas_limit): comment += "Y" - flag = True + flag = True if flag: #print 'LBL %3.1f (%3.1f) %3.1f (%3.1f)' %(X, RFrefX, Y, RFrefY) sendToPVSS(comment, "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) bad_lba[partNr] = 1 - + elif msgType == 'DOWN': sendToPVSS("down", "LOFAR_PIC_LBA%03d" %(partNr), State['BROKEN']) bad_lba[partNr] = 1 - + if part == 'HBA': if msgType == 'LOW_NOISE': if float(keyinfo.get('Xproc','0.0')) >= 100.0 or float(keyinfo.get('Yproc','0.0')) >= 100.0: sendToPVSS("low-noise", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'HIGH_NOISE': limits = args.get('N','0.0') proc_limit_2 = 0.0 @@ -349,7 +338,7 @@ def addDataToPVSS(data): diff_limit = float(limits[2]) else: proc_limit_1 = float(limits) - + if float(keyinfo.get('Xproc','0.0')) >= proc_limit_1 or float(keyinfo.get('Yproc','0.0')) >= proc_limit_1: if ((float(keyinfo.get('Xproc','0.0')) < proc_limit_2 and (float(keyinfo.get('Xval','0.0')) - float(keyinfo.get('Xref','0.0'))) < diff_limit) and (float(keyinfo.get('Yproc','0.0')) < proc_limit_2 and (float(keyinfo.get('Yval','0.0')) - float(keyinfo.get('Yref','0.0'))) < diff_limit)): @@ -368,7 +357,7 @@ def addDataToPVSS(data): diff_limit = float(limits[2]) else: proc_limit_1 = float(limits) - + if float(keyinfo.get('Xproc','0.0')) >= proc_limit_1 or float(keyinfo.get('Yproc','0.0')) >= proc_limit_1: if ((float(keyinfo.get('Xproc','0.0')) < proc_limit_2 and float(keyinfo.get('Xdiff','0.0')) < diff_limit) and (float(keyinfo.get('Yproc','0.0')) < proc_limit_2 and float(keyinfo.get('Ydiff','0.0')) < diff_limit)): @@ -376,26 +365,26 @@ def addDataToPVSS(data): else: sendToPVSS("jitter", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'OSCILLATION': if not args.has_key('O'): sendToPVSS("oscillating", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'C_SUMMATOR': sendToPVSS("modem-fail", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'SUMMATOR_NOISE': if not args.has_key('SN'): sendToPVSS("summator-noise", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'SPURIOUS': if not args.has_key('SP'): sendToPVSS("spurious-signals", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'RF_FAIL': flag = False limit = float(args.get('S','0')) @@ -406,11 +395,11 @@ def addDataToPVSS(data): flag = True if len(Y): if abs(float(Y[0]) - float(Y[2])) > limit: - flag = True - if flag: + flag = True + if flag: sendToPVSS("rf-tile-fail", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - + elif msgType == 'E_FAIL': if args.has_key('E') == False: max_errors = 2 @@ -419,7 +408,7 @@ def addDataToPVSS(data): LNY_errors = 0 RFX_errors = 0 RFY_errors = 0 - + # check first total number of errors in tile for elem_nr in range(1,17,1): if keyinfo.has_key('M%d' %(elem_nr)): @@ -431,65 +420,64 @@ def addDataToPVSS(data): if keyinfo.has_key('X%d' %(elem_nr)): RFX_errors += 1 if keyinfo.has_key('Y%d' %(elem_nr)): - RFY_errors += 1 - + RFY_errors += 1 - send_tile_errors = 0 + + send_tile_errors = 0 for elem_nr in range(1,17,1): send_elem_errors = 0 - + if modem_errors > max_errors and keyinfo.has_key('M%d' %(elem_nr)): sendToPVSS("rf-fail", "LOFAR_PIC_HBA%02d.element%02d.comm" %(partNr, elem_nr-1), State['BROKEN']) send_elem_errors += 1 - + comment = "" if (RFX_errors > max_errors) and keyinfo.has_key('X%d' %(elem_nr)): comment += "rf-fail&" - + if (LNX_errors > max_errors) and keyinfo.has_key('LNX%d' %(elem_nr)): comment += "low-noise&" - + if keyinfo.has_key('HNX%d' %(elem_nr)) or keyinfo.has_key('JX%d' %(elem_nr)): comment += "noise&" - + if len(comment) > 0: sendToPVSS(comment[:-1], "LOFAR_PIC_HBA%02d.element%02d.X" %(partNr, elem_nr-1), State['BROKEN']) send_elem_errors += 1 - - + + comment = "" if (RFY_errors > max_errors) and keyinfo.has_key('Y%d' %(elem_nr)): comment += "rf-fail&" - + if (LNY_errors > max_errors) and keyinfo.has_key('LNY%d' %(elem_nr)): comment += "low-noise&" - + if keyinfo.has_key('HNY%d' %(elem_nr)) or keyinfo.has_key('JY%d' %(elem_nr)): comment += "noise&" - - if len(comment) > 0: + + if len(comment) > 0: sendToPVSS(comment[:-1], "LOFAR_PIC_HBA%02d.element%02d.Y" %(partNr, elem_nr-1), State['BROKEN']) send_elem_errors += 1 - - + + if send_elem_errors > 0: sendToPVSS("rf-fail", "LOFAR_PIC_HBA%02d.element%02d" %(partNr, elem_nr-1), State['BROKEN']) send_tile_errors += 1 - + if send_tile_errors > 0: sendToPVSS("", "LOFAR_PIC_HBA%02d" %(partNr), State['BROKEN']) bad_hba[partNr] = 1 - - return (list(bad_lba), list(bad_hba)) -# write bad rcu's to file in logdir + return (list(bad_lba), list(bad_hba)) + +# write bad rcu's to file in report_dir def addDataToBadRcuFile(bad_lba, bad_hba): - global nLBL - global nLBH - - # add bad rcus to file - filename = '%s_bad_rcus.txt' %(getHostName()) - full_filename = os.path.join(logdir, filename) + # add bad rcus to file + report_dir = conf().as_string('paths.local-report-dir') + filename = '%s_bad_rcus.txt' % hostname + full_filename = os.path.join(report_dir, filename) + print "bad_rcus filename = %s" % full_filename f = open(full_filename, 'w') lbl = "" @@ -501,7 +489,7 @@ def addDataToBadRcuFile(bad_lba, bad_hba): else: lbh += "%d," %(ant*2) lbh += "%d," %(ant*2+1) - + if len(lbl): lbl = lbl[:-1] lbl = "LBL=[" + lbl + "]\n" @@ -520,9 +508,9 @@ def addDataToBadRcuFile(bad_lba, bad_hba): hba = hba[:-1] hba = "HBA=[" + hba + "]\n" f.write(hba) - + f.close() if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/LTA/LTAIngest/CMakeLists.txt b/LTA/LTAIngest/CMakeLists.txt index 733f28a62c3054edad6fc6be0532f89d18548810..ff4ead9bc0493dd74e32d22180839a275b0e4d41 100644 --- a/LTA/LTAIngest/CMakeLists.txt +++ b/LTA/LTAIngest/CMakeLists.txt @@ -276,7 +276,8 @@ install(PROGRAMS ${STARTUP_SCRIPTS} DESTINATION ${PYTHON_INSTALL_DIR}/LTAIngest) #deploy CHECKSUM_PROGS in same python ingest lib dir set(CHECKSUM_PROGS md5adler/a32 - md5adler/md5a32) + md5adler/md5a32 + md5adler/md5a32bc) install(PROGRAMS ${CHECKSUM_PROGS} DESTINATION ${PYTHON_INSTALL_DIR}/LTAIngest/md5adler) #determine LTAINGEST_LOG_ROOT_DIR for various build hosts (used in ingest_config.py.in) diff --git a/LTA/LTAIngest/ingest_config.py.in b/LTA/LTAIngest/ingest_config.py.in index e39b43b8a6ed4822dda4f8a9e8aad20c0a8b9bef..3ae0d647c5a1fb650772fd39da38bbdb131493b7 100644 --- a/LTA/LTAIngest/ingest_config.py.in +++ b/LTA/LTAIngest/ingest_config.py.in @@ -21,7 +21,7 @@ ltaClient = None host = socket.gethostname() ## Using netifaces module or something similar would be nicer, but it doesn't want to install in a custom dir ## So we use this hack -if 'lexar' in host: +if 'lexar00' in host: host = host + '.offline.lofar' if 'gridftp01.target.rug.nl' in host: host = 'lotar2.staging.lofar' @@ -120,9 +120,11 @@ momURLlogout = 'https://lcs029.control.lofar:8443/useradministration/user/logout momRetry = 3 srmRetry = 2 -if 'lexar' in host: +if 'lexar001' in host or 'lexar002' in host: srmInit = '/globalhome/ingest/service/bin/init.sh' -if 'lotar' in host: +elif 'lexar003' in host or 'lexar004' in host: + srmInit = '/globalhome/ingest/.grid/.ingest_profile' +elif 'lotar' in host: srmInit = '/home/lofarlocal/ltacp/bin/init.sh' try: diff --git a/LTA/LTAIngest/ingestpipeline.py b/LTA/LTAIngest/ingestpipeline.py index 8dede787e44d286e159ef5435fe00354785df2e9..c93c2cc672b8b877a1b2a80af42d58e979d856e7 100755 --- a/LTA/LTAIngest/ingestpipeline.py +++ b/LTA/LTAIngest/ingestpipeline.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import logging, os, time, xmlrpclib, subprocess, random, unspecifiedSIP import socket +import re from lxml import etree from cStringIO import StringIO from job_group import corr_type, bf_type, img_type, unspec_type, pulp_type @@ -18,7 +19,7 @@ def humanreadablesize(num, suffix='B'): return "%.1f%s%s" % (num, 'Y', suffix) except TypeError: return str(num) - + IngestStarted = 10 ## 20 not used IngestSIPComplete = 30 @@ -40,7 +41,7 @@ class PipelineError(Exception): #---------------------- IngestPipeline ------------------------------------------ class IngestPipeline(): - def __init__(self, logdir, job, momClient, ltaClient, ltacphost, ltacpport, mailCommand, momRetry, ltaRetry, srmRetry, srmInit): + def __init__(self, logdir, job, momClient, ltaClient, ltacphost, ltacpport, mailCommand, momRetry, ltaRetry, srmRetry, srmInit, user=getpass.getuser()): self.logdir = logdir self.job = job self.momClient = momClient @@ -48,6 +49,7 @@ class IngestPipeline(): self.ltacphost = ltacphost self.ltacpport = ltacpport self.mailCommand = mailCommand + self.user = user self.Project = job['Project'] self.DataProduct = job['DataProduct'] @@ -69,10 +71,15 @@ class IngestPipeline(): if 'summary' in self.DataProduct: self.FileType = pulp_type self.JobId = job['JobId'] - self.ArchiveId = int(job['ArchiveId']) + self.ArchiveId = int(job['ArchiveId']) self.ObsId = int(job['ObservationId']) self.HostLocation = job['Location'].split(':')[0] self.Location = job['Location'].split(':')[1] + if 'cep4' in self.HostLocation.lower(): + # lexar003/lexar004 have cep4 lustrefs /data dir mounted on /data, + # so we can read data with ltacp client from localhost + self.HostLocation = 'localhost' + pos = self.Location.find(self.DataProduct) if pos > 0: ## trick to support tar files with different names self.LocationDir = self.Location[:pos] @@ -101,6 +108,7 @@ class IngestPipeline(): self.ltaRetry = ltaRetry self.srmRetry = srmRetry self.status = IngestStarted + self.finishedSuccessfully = True ## Set logger self.logger =logging.getLogger('Slave') @@ -108,6 +116,7 @@ class IngestPipeline(): def GetStorageTicket(self): try: + self.logger.debug("calling GetStorageTicket(%s, %s, %s, %s, %s, %s, True, %s)", self.Project, self.FileName, self.FileSize, self.ArchiveId, self.JobId, self.ObsId, self.Type) start = time.time() result = self.ltaClient.GetStorageTicket(self.Project, self.FileName, self.FileSize, self.ArchiveId, self.JobId, self.ObsId, True, self.Type) self.logger.debug("GetStorageTicket for %s took %ds" % (self.JobId, time.time() - start)) @@ -166,20 +175,41 @@ class IngestPipeline(): def TransferFile(self): self.logger.debug('Starting file transfer') hostname = socket.getfqdn() + self.logger.debug('%s: hostname=%s HostLocation=%s', self.JobId, hostname, self.HostLocation) javacmd = "java" - if "lexar" in hostname: + if "lexar001" in hostname or "lexar002" in hostname: javacmd = "/data/java7/jdk1.7.0_55/bin/java" ltacppath = "/globalhome/%s/ltacp" % ("ingesttest" if self.ltacpport == 8801 else "ingest") - - if self.PrimaryUri: - cmd = ["ssh", "-T", "ingest@" +self.HostLocation, "cd %s;%s -Xmx256m -cp %s/qpid-properties/lexar001.offline.lofar:%s/ltacp.jar nl.astron.ltacp.client.LtaCp %s %s %s %s" % (self.LocationDir, javacmd, ltacppath, ltacppath, hostname, self.ltacpport, self.PrimaryUri, self.Source)] + use_shell = False + + if self.HostLocation == 'localhost': + # copy data with ltacp client from HostLocation localhost, to ltacpserver at localhost + # so no need for ssh + use_shell = ("lexar003" in hostname or "lexar004" in hostname) + hostname = 'localhost' + cmd = ["cd %s && %s -Xmx256m -cp %s/qpid-properties/lexar001.offline.lofar:%s/ltacp.jar nl.astron.ltacp.client.LtaCp %s %s %s %s" % (self.LocationDir, javacmd, ltacppath, ltacppath, hostname, self.ltacpport, self.PrimaryUri, self.Source)] else: - cmd = ["ssh", "-T", "ingest@" + self.HostLocation, "cd %s;%s -Xmx256m -cp %s/qpid-properties/lexar001.offline.lofar:%s/ltacp.jar nl.astron.ltacp.client.LtaCp %s %s %s/%s %s" % (self.LocationDir, javacmd, ltacppath, ltacppath, hostname, self.ltacpport, self.tempPrimary, self.FileName, self.Source)] + if "lexar003" in hostname or "lexar004" in hostname: + #lexar003/004 are not aware of cep2, and cep2 is not aware of lexar003/004, + #so make sure we can handle the proper hostnames and ips + if self.HostLocation.startswith('locus') and not self.HostLocation.endswith('cep2.lofar'): + hostname = '10.178.1.3' if "lexar003" in hostname else '10.178.1.4' + self.HostLocation += '.cep2.lofar' + else: + use_shell=True + + # copy data with ltacp from a remote host, so use ssh + if self.PrimaryUri: + cmd = ["ssh", "-T", "ingest@" +self.HostLocation, "cd %s;%s -Xmx256m -cp %s/qpid-properties/lexar001.offline.lofar:%s/ltacp.jar nl.astron.ltacp.client.LtaCp %s %s %s %s" % (self.LocationDir, javacmd, ltacppath, ltacppath, hostname, self.ltacpport, self.PrimaryUri, self.Source)] + else: + cmd = ["ssh", "-T", "ingest@" + self.HostLocation, "cd %s;%s -Xmx256m -cp %s/qpid-properties/lexar001.offline.lofar:%s/ltacp.jar nl.astron.ltacp.client.LtaCp %s %s %s/%s %s" % (self.LocationDir, javacmd, ltacppath, ltacppath, hostname, self.ltacpport, self.tempPrimary, self.FileName, self.Source)] + + self.logger.debug('%s: hostname=%s HostLocation=%s, use_shell=%s', self.JobId, hostname, self.HostLocation, use_shell) ## SecondaryUri handling not implemented - self.logger.debug(cmd) + self.logger.debug(' '.join(cmd)) start = time.time() - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=use_shell) logs = p.communicate() elapsed = time.time() - start self.logger.debug("File transfer for %s took %d sec" % (self.JobId, elapsed)) @@ -190,7 +220,7 @@ class IngestPipeline(): self.logger.debug('Shell command for %s exited with code %s' % (self.JobId, p.returncode)) self.logger.debug('STD ERR of TransferFile command for %s:\n%s' % (self.JobId, logs[1])) self.logger.debug(log) - if (not 'No such file or directory.' in logs[1]) and (not 'does not exist' in logs[0]): + if (not 'No such file or directory' in logs[1]) and (not 'does not exist' in logs[0]): if not self.ParseLTAcpLog(log): self.logger.error("Parsing ltacp result failed for %s" % self.JobId) raise Exception('File transfer failed of %s' % self.JobId) @@ -227,7 +257,8 @@ class IngestPipeline(): cp = ltacp.LtaCp(host, os.path.join(self.LocationDir, self.Source), - self.PrimaryUri) + self.PrimaryUri, + self.user) self.MD5Checksum, self.Adler32Checksum, self.FileSize = cp.transfer() @@ -244,12 +275,13 @@ class IngestPipeline(): except Exception: pass - except ltacp.LtacpException as exp: - if '550 File not found' in exp.value: - self.logger.error('Destination directory does not exist. Creating %s in LTA for %s' % (self.PrimaryUri, self.JobId)) + except Exception as exp: + if isinstance(exp, ltacp.LtacpException): + if '550 File not found' in exp.value: + self.logger.error('Destination directory does not exist. Creating %s in LTA for %s' % (self.PrimaryUri, self.JobId)) - if ltacp.create_missing_directories(self.PrimaryUri) == 0: - self.logger.info('Created path %s in LTA for %s' % (self.PrimaryUri, self.JobId)) + if ltacp.create_missing_directories(self.PrimaryUri) == 0: + self.logger.info('Created path %s in LTA for %s' % (self.PrimaryUri, self.JobId)) raise Exception('New style file transfer failed of %s\n%s' % (self.JobId, str(exp))) @@ -360,7 +392,7 @@ class IngestPipeline(): if storageTickets[0].text != str(self.ticket): self.logger.error("CheckSIPContent for %s storageTicket %s does not match expected %s" % (self.JobId, storageTickets[0].text, self.ticket)) return False - + self.logger.debug("CheckSIPContent OK for %s" % (self.JobId,)) return True except Exception as e: @@ -381,11 +413,15 @@ class IngestPipeline(): self.logger.debug('SIP received for %s from MoM with size %d (%s): %s' % (self.JobId, len(self.SIP), humanreadablesize(len(self.SIP)), self.SIP[0:256])) elif self.Type.lower() == "eor": try: - sip_host = job['SIPLocation'].split(':')[0] - for i in range(1, 43): + sip_host = self.job['SIPLocation'].split(':')[0] + sip_path = self.job['SIPLocation'].split(':')[1] + + # eor dawn nodes are not known in dns + # convert to ip address + for i in range(1, 33): sip_host = sip_host.replace('node%d.intra.dawn.rug.nl' % (i+100,), '10.196.232.%d' % (i+10,)) - sip_path = job['SIPLocation'].split(':')[1] - cmd = ['ssh', '-tt', '-n', '-x', '-q', '%s@%s' % (getpass.getuser(), sip_host), 'cat %s' % sip_path] + + cmd = ['ssh', '-tt', '-n', '-x', '-q', '%s@%s' % (self.user, sip_host), 'cat %s' % sip_path] self.logger.debug("GetSIP for %s with mom2DPId %s - StorageTicket %s - FileName %s - Uri %s - cmd %s" % (self.JobId, self.ArchiveId, self.ticket, self.FileName, self.PrimaryUri, ' ' .join(cmd))) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() @@ -442,6 +478,8 @@ class IngestPipeline(): self.SIP = sip_dom.toxml("utf-8") self.SIP = self.SIP.replace('<stationType>Europe</stationType>','<stationType>International</stationType>') + #make sure the source in the SIP is the same as the type of the storageticket + self.SIP = re.compile('<source>eor</source>', re.IGNORECASE).sub('<source>%s</source>' % (self.Type,), self.SIP) except: self.logger.exception('Getting SIP from EoR failed') raise @@ -508,6 +546,7 @@ class IngestPipeline(): ## function raised PipelineError itself. Assume retries not useful raise except Exception as e: + self.logger.error('Exception for %s in %s: %s', self.JobId, func.__name__, e) error += '\n' + str(e) else: if retry: @@ -515,10 +554,10 @@ class IngestPipeline(): else: self.logger.debug(errortext + ' ran without a problem on %s' % self.JobId) error = '' - break + break retry += 1 if retry < times: - wait_time = random.randint(30, 60) * retry + wait_time = random.randint(5, 30) * retry self.logger.debug('waiting %d seconds before trying %s again' % (wait_time, self.JobId)) time.sleep(wait_time) if error: @@ -530,11 +569,10 @@ class IngestPipeline(): start = time.time() self.RetryRun(self.GetStorageTicket, self.ltaRetry, 'Getting storage ticket') - self.RetryRun(self.TransferFileNew, self.srmRetry , 'Transfering file') - #if self.Type.lower() == "eor": - #self.RetryRun(self.TransferFileNew, self.srmRetry , 'Transfering file') - #else: - #self.RetryRun(self.TransferFile, self.srmRetry , 'Transfering file') + if self.Type.lower() == "eor": + self.RetryRun(self.TransferFileNew, self.srmRetry , 'Transfering file') + else: + self.RetryRun(self.TransferFile, self.srmRetry , 'Transfering file') self.RetryRun(self.SendChecksums, self.ltaRetry, 'Sending Checksums') # self.RenameFile() @@ -548,7 +586,8 @@ class IngestPipeline(): self.logger.debug("Ingest Pipeline finished for %s in %d sec with average speed of %s for %s including all overhead" % (self.JobId, elapsed, humanreadablesize(avgSpeed, 'Bps'), humanreadablesize(float(self.FileSize), 'B'))) except Exception: self.logger.debug("Ingest Pipeline finished for %s in %d sec" % (self.JobId, elapsed)) - + self.finishedSuccessfully = True + except PipelineError as pe: self.logger.debug('Encountered PipelineError for %s : %s' % (self.JobId, str(pe))) ## roll back transfer if necessary @@ -560,7 +599,7 @@ class IngestPipeline(): try: if self.ticket: self.RetryRun(self.SendStatus, self.ltaRetry, 'Setting LTA status', IngestFailed) - except Exception as e: + except Exception as e: os.system('echo "Received unknown exception in SendStatus for %s to %s while handling another error:\n%s\n\nCheck LTA catalog and SRM!\n%s"|mailx -s "Warning: LTA catalog status update failed" ' % (self.JobId, IngestFailed, self._hidePassword(str(e)), self.PrimaryUri) + self.mailCommand) self.logger.error('Sent Mail: LTA catalog status update failed to ' + self.mailCommand) self.logger.exception('SendStatus IngestFailed failed') @@ -590,7 +629,7 @@ class IngestPipeline(): if self.ticket: self.RetryRun(self.SendStatus, self.ltaRetry, 'Setting LTA status', IngestFailed) raise - + def _hidePassword(self, message): ''' helper function which hides the password in the ltaClient url in the message ''' @@ -600,35 +639,160 @@ class IngestPipeline(): return message.replace(':'+password, ':HIDDENPASSWORD') except Exception as e: return message - + #----------------------------------------------------------------- selfstarter - if __name__ == '__main__': import sys - if len(sys.argv) != 2: + import threading + import os + import os.path + import job_parser + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser("%prog [options] <jobfile/dir_with_job_files>", + description='Run the ingestpipeline on a single jobfile, or a directory containing many jobfiles.') + parser.add_option("-u", "--user", dest="user", type="string", default=getpass.getuser(), help="username for to login on <host>, [default: %default]") + parser.add_option("-p", "--parallel", dest="maxParallelJobs", type="int", default=4, help="number of parellel pipelines to run when processing a directory of jobfiles [default: %default]") + parser.add_option("", "--odd", dest="odd", action="store_true", default=None, help="only process dataproducts with an odd subband number, [default: %default]") + parser.add_option("", "--even", dest="even", action="store_true", default=None, help="only process dataproducts with an even subband number, [default: %default]") + (options, args) = parser.parse_args() + + if len(args) != 1: + parser.print_help() + sys.exit(1) + print 'usage: ingestpipeline.py <path_to_jobfile.xml>' + print 'or' + print 'usage: ingestpipeline.py <path_to_dir_containing_set_of_jobfiles>' logging.basicConfig(format="%(asctime)-15s %(levelname)s %(message)s", level=logging.DEBUG) logger = logging.getLogger('Slave') - jobfile = sys.argv[1] - import job_parser - parser = job_parser.parser(logger) - job = parser.parse(jobfile) - job['filename'] = jobfile - - logger.info(str(job)) - - if getpass.getuser() == 'ingest': + if options.user == 'ingest': import ingest_config as config else: import ingest_config_test as config - #create misc mock args - ltacphost = 'mock-ltacphost' - ltacpport = -1 - mailCommand = '' - srmInit = '' + path = args[0] + + if os.path.isfile(path): + parser = job_parser.parser(logger) + job = parser.parse(path) + job['filename'] = path + logger.info("Parsed jobfile %s" % (path,)) + jobPipeline = IngestPipeline(None, job, config.momClient, config.ltaClient, config.ipaddress, config.ltacpport, config.mailCommand, config.momRetry, config.ltaRetry, config.srmRetry, config.srmInit) + jobPipeline.run() + exit(0) - standalone = IngestPipeline(None, job, config.momClient, config.ltaClient, config.ipaddress, config.ltacpport, config.mailCommand, config.momRetry, config.ltaRetry, config.srmRetry, config.srmInit) - standalone.run() + if os.path.isdir(path): + dirEntries = [os.path.join(path, x) for x in os.listdir(path)] + jobfilepaths = [p for p in dirEntries if os.path.isfile(p)] + + try: + if not os.path.exists(os.path.join(path, 'done')): + os.mkdir(os.path.join(path, 'done')) + except Exception as e: + logger.error(str(e)) + exit(-1) + + # scan all jobfilepaths + # put each job in hostJobQueues dict, in a list per host + # this gives us a job queue per host, so we can parallelize. + hostJobQueues = {} + for jobfilepath in jobfilepaths: + try: + parser = job_parser.parser(logger) + job = parser.parse(jobfilepath) + job['filename'] = jobfilepath + logger.info("Parsed jobfile %s" % (jobfilepath,)) + + if options.odd or options.even: + # parse subband number of dataproduct + # only add this job if the subband number odd/even complies with given option + subband = next(x for x in job['DataProduct'].split('_') if x.startswith('SB')) + subband_nr = int(filter(str.isdigit,subband)) + is_even = subband_nr % 2 == 0 + if (options.odd and is_even) or (options.even and not is_even): + continue + + if 'host' in job: + host = job['host'] + if not host in hostJobQueues: + hostJobQueues[host] = [] + hostJobQueues[host].append(job) + except Exception as e: + logger.error("Could not parse %s: %s" % (jobfilepath, str(e))) + + runningPipelinesPerHost = {} + + while hostJobQueues: + busyHosts = set(runningPipelinesPerHost.keys()) + freeHosts = set(hostJobQueues.keys()) - busyHosts + + # sort free host with longest queues first, so their jobs get started first + freeHosts = sorted(freeHosts, key=lambda h: len(hostJobQueues[h]), reverse=True) + + startedNewJobs = False + + # start new pipeline for one job per free host + for host in freeHosts: + jobQueue = hostJobQueues[host] + + if not jobQueue: + logging.info('no more jobs for host: %s' % host) + del hostJobQueues[host] + elif len(runningPipelinesPerHost) < options.maxParallelJobs: + jobToRun = jobQueue.pop(0) + + logging.info('starting job %s on host %s' % (jobToRun['JobId'], host)) + jobPipeline = IngestPipeline(None, jobToRun, config.momClient, config.ltaClient, config.ipaddress, config.ltacpport, config.mailCommand, config.momRetry, config.ltaRetry, config.srmRetry, config.srmInit, options.user) + + def runPipeline(pl): + try: + pl.run() + except Exception as e: + logger.error(str(e)) + + pipelineThread = threading.Thread(target=runPipeline, kwargs={'pl':jobPipeline}) + pipelineThread.daemon = True + pipelineThread.start() + + runningPipelinesPerHost[host] = (jobPipeline, pipelineThread) + logging.info('started job %s on host %s' % (jobToRun['JobId'], host)) + startedNewJobs = True + + if startedNewJobs: + for h, q in hostJobQueues.items(): + logger.info("%s: %s jobs in queue" % (h, len(q))) + + # check which jobs are done + busyHosts = set(runningPipelinesPerHost.keys()) + + for host in busyHosts: + try: + jobPipeline, pipelineThread = runningPipelinesPerHost[host] + + if pipelineThread.isAlive(): + logging.info('job %s on host %s is running' % (jobPipeline.job['JobId'], host)) + else: + logging.info('job %s on host %s is done' % (jobPipeline.job['JobId'], host)) + pipelineThread.join(10) + del runningPipelinesPerHost[host] + + if jobPipeline.finishedSuccessfully: + try: + if os.path.isdir(path): + path, filename = os.path.split(jobPipeline.job['filename']) + os.rename(jobPipeline.job['filename'], os.path.join(path, 'done', filename)) + except Exception as e: + logger.error(str(e)) + else: + #retry, put back in jobqueue + logging.info('job %s on host %s was put back in the queue' % (jobPipeline.job['JobId'], host)) + hostJobQueues[host].append(jobPipeline.job) + except Exception as e: + logger.error(str(e)) + + time.sleep(30) diff --git a/LTA/LTAIngest/ingestpipeline_test.py b/LTA/LTAIngest/ingestpipeline_test.py index 7b04ab91942559b018a5954a08054ec7db30294e..3bf5b75415c24bab0ee11dd5be1c53799ee43c8f 100755 --- a/LTA/LTAIngest/ingestpipeline_test.py +++ b/LTA/LTAIngest/ingestpipeline_test.py @@ -205,7 +205,7 @@ class IngestPipeline(): ## self.logger.debug('Shell command for %s exited with code %s' % (self.JobId, p.returncode)) self.logger.debug('STD ERR of TransferFile command for %s:\n%s' % (self.JobId, logs[1])) self.logger.debug(log) - if (not 'No such file or directory.' in logs[1]) and (not 'does not exist' in logs[0]): + if (not 'No such file or directory' in logs[1]) and (not 'does not exist' in logs[0]): if not self.ParseLTAcpLog(log): self.logger.error("Parsing ltacp result failed for %s" % self.JobId) raise Exception('File transfer failed of %s' % self.JobId) diff --git a/LTA/LTAIngest/ltacp.py b/LTA/LTAIngest/ltacp.py index 6f6b0554257560b06734352e74499935d7c96e98..9cb636433f08d80ad1d092c4920c7e75301f0448 100755 --- a/LTA/LTAIngest/ltacp.py +++ b/LTA/LTAIngest/ltacp.py @@ -8,6 +8,7 @@ # adler32 is used between localhost and the SRM. import logging +from optparse import OptionParser from subprocess import Popen, PIPE import socket import os, sys, getpass @@ -33,15 +34,6 @@ logger = logging.getLogger('Slave') _ingest_init_script = '/globalhome/ingest/service/bin/init.sh' -if __name__ == '__main__': - log_handler = logging.StreamHandler() - formatter = logging.Formatter('%(asctime)-15s %(levelname)s %(message)s') - formatter.converter = time.gmtime - log_handler.setFormatter(formatter) - logger.addHandler(log_handler) - logger.setLevel(logging.INFO) - - class LtacpException(Exception): def __init__(self, value): self.value = value @@ -57,7 +49,6 @@ def getLocalIPAddress(): def convert_surl_to_turl(surl): #list of sara doors is based on recent actual transfers using srmcp, which translates the surl to a 'random' turl sara_nodes = ['fly%d' % i for i in range(1, 11)] + \ - ['wasp%d' % i for i in range(1, 10)] + \ ['by27-%d' % i for i in range(1, 10)] + \ ['bw27-%d' % i for i in range(1, 10)] + \ ['by32-%d' % i for i in range(1, 10)] + \ @@ -72,6 +63,7 @@ def convert_surl_to_turl(surl): turl = turl.replace("srm://lofar-srm.fz-juelich.de", "gsiftp://dcachepool%d.fz-juelich.de:2811" % (random.randint(9, 16),), 1) turl = turl.replace("srm://srm.target.rug.nl:8444","gsiftp://gridftp02.target.rug.nl/target/gpfs2/lofar/home/srm",1) turl = turl.replace("srm://srm.target.rug.nl","gsiftp://gridftp02.target.rug.nl/target/gpfs2/lofar/home/srm",1) + turl = turl.replace("srm://lta-head.lofar.psnc.pl:8443", "gsiftp://door0%d.lofar.psnc.pl:2811" % (random.randint(1, 2),), 1) return turl def createNetCatCmd(user, host): @@ -79,7 +71,7 @@ def createNetCatCmd(user, host): # nc has no version option or other ways to check it's version # so, just try the variants and pick the first one that does not fail - nc_variants = ['nc --send-only', 'nc -q 0'] + nc_variants = ['nc --send-only', 'nc -q 0', 'nc'] for nc_variant in nc_variants: cmd = ['ssh', '-n', '-x', '%s@%s' % (user, host), nc_variant] @@ -124,6 +116,26 @@ class LtaCp: dst_turl = convert_surl_to_turl(self.dst_surl) logger.info('ltacp %s: initiating transfer of %s:%s to %s' % (self.logId, self.src_host, self.src_path_data, self.dst_surl)) + # get input datasize + cmd_remote_du = self.ssh_cmd + ['du -b --max-depth=0 %s' % (self.src_path_data,)] + logger.info('ltacp %s: remote getting datasize. executing: %s' % (self.logId, ' '.join(cmd_remote_du))) + p_remote_du = Popen(cmd_remote_du, stdout=PIPE, stderr=PIPE) + self.started_procs[p_remote_du] = cmd_remote_du + + # block until du is finished + output_remote_du = p_remote_du.communicate() + del self.started_procs[p_remote_du] + if p_remote_du.returncode != 0: + raise LtacpException('ltacp %s: remote du failed: \nstdout: %s\nstderr: %s' % (self.logId, + output_remote_du[0], + output_remote_du[1])) + # compute various parameters for progress logging + input_datasize = int(output_remote_du[0].split()[0]) + logger.info('ltacp %s: input datasize: %d bytes, %s' % (self.logId, input_datasize, humanreadablesize(input_datasize))) + estimated_tar_size = 512*(input_datasize / 512) + 3*512 #512byte header, 2*512byte ending, 512byte modulo data + logger.info('ltacp %s: estimated_tar_size: %d bytes, %s' % (self.logId, estimated_tar_size, humanreadablesize(estimated_tar_size))) + + #--- # Server part #--- @@ -133,58 +145,28 @@ class LtaCp: random.seed(hash(self.src_path_data) ^ hash(time.time())) p_data_in, port_data = self._ncListen('data') - p_md5_receive, port_md5 = self._ncListen('md5 checksums') - - # create fifo paths - self.local_fifo_basename = '/tmp/ltacp_datapipe_%s_%s' % (self.src_host, self.logId) - - def createLocalFifo(fifo_postfix): - fifo_path = '%s_%s' % (self.local_fifo_basename, fifo_postfix) - logger.info('ltacp %s: creating data fifo: %s' % (self.logId, fifo_path)) - if os.path.exists(fifo_path): - os.remove(fifo_path) - os.mkfifo(fifo_path) - if not os.path.exists(fifo_path): - raise LtacpException("ltacp %s: Could not create fifo: %s" % (self.logId, fifo_path)) - self.fifos.append(fifo_path) - return fifo_path - - self.local_data_fifo = createLocalFifo('globus_url_copy') - self.local_byte_count_fifo = createLocalFifo('local_byte_count') - self.local_adler32_fifo = createLocalFifo('local_adler32') - - # tee incoming data stream to fifo (and pipe stream in tee_proc.stdout) - def teeDataStreams(pipe_in, fifo_out): - cmd_tee = ['tee', fifo_out] - logger.info('ltacp %s: splitting datastream. executing: %s' % (self.logId, ' '.join(cmd_tee),)) - tee_proc = Popen(cmd_tee, stdin=pipe_in, stdout=PIPE, stderr=PIPE) - self.started_procs[tee_proc] = cmd_tee - return tee_proc - - p_tee_data = teeDataStreams(p_data_in.stdout, self.local_data_fifo) - p_tee_byte_count = teeDataStreams(p_tee_data.stdout, self.local_byte_count_fifo) - p_tee_checksums = teeDataStreams(p_tee_byte_count.stdout, self.local_adler32_fifo) - - # start counting number of bytes in incoming data stream - cmd_byte_count = ['wc', '-c', self.local_byte_count_fifo] - logger.info('ltacp %s: computing byte count. executing: %s' % (self.logId, ' '.join(cmd_byte_count))) - p_byte_count = Popen(cmd_byte_count, stdout=PIPE, stderr=PIPE, env=dict(os.environ, LC_ALL="C")) - self.started_procs[p_byte_count] = cmd_byte_count - - # start computing md5 checksum of incoming data stream - cmd_md5_local = ['md5sum'] - logger.info('ltacp %s: computing local md5 checksum. executing on data pipe: %s' % (self.logId, ' '.join(cmd_md5_local))) - p_md5_local = Popen(cmd_md5_local, stdin=p_tee_checksums.stdout, stdout=PIPE, stderr=PIPE) - self.started_procs[p_md5_local] = cmd_md5_local - - # start computing adler checksum of incoming data stream - cmd_a32_local = ['./md5adler/a32', self.local_adler32_fifo] - #cmd_a32_local = ['md5sum', self.local_adler32_fifo] - logger.info('ltacp %s: computing local adler32 checksum. executing: %s' % (self.logId, ' '.join(cmd_a32_local))) - p_a32_local = Popen(cmd_a32_local, stdout=PIPE, stderr=PIPE) - self.started_procs[p_a32_local] = cmd_a32_local - - # start copy fifo stream to SRM + + + self.local_data_fifo = '/tmp/ltacp_datapipe_%s_%s' % (self.src_host, self.logId) + + logger.info('ltacp %s: creating data fifo: %s' % (self.logId, self.local_data_fifo)) + if os.path.exists(self.local_data_fifo): + os.remove(self.local_data_fifo) + os.mkfifo(self.local_data_fifo) + if not os.path.exists(self.local_data_fifo): + raise LtacpException("ltacp %s: Could not create fifo: %s" % (self.logId, self.local_data_fifo)) + + # transfer incomming data stream via md5a32bc to compute md5, adler32 and byte_count + # data is written to fifo, which is then later fed into globus-url-copy + # on stdout we can monitor progress + # set progress message step 0f 0.5% of estimated_tar_size + currdir = os.path.dirname(os.path.realpath(__file__)) + cmd_md5a32bc = [currdir + '/md5adler/md5a32bc', '-p', str(estimated_tar_size/200), self.local_data_fifo] + logger.info('ltacp %s: processing data stream for md5, adler32 and byte_count. executing: %s' % (self.logId, ' '.join(cmd_md5a32bc),)) + p_md5a32bc = Popen(cmd_md5a32bc, stdin=p_data_in.stdout, stdout=PIPE, stderr=PIPE) + self.started_procs[p_md5a32bc] = cmd_md5a32bc + + # start copy fifo stream to globus-url-copy guc_options = ['-cd', #create remote directories if missing '-p 4', #number of parallel ftp connections '-bs 131072', #buffer size @@ -221,7 +203,9 @@ class LtaCp: # 3) simultaneously to 2), calculate checksum of fifo stream # 4) break fifo - self.remote_data_fifo = '/tmp/ltacp_md5_pipe_%s_%s' % (self.logId, port_md5) + self.remote_data_fifo = '/tmp/ltacp_md5_pipe_%s' % (self.logId, ) + #make sure there is no old remote fifo + self._removeRemoteFifo() cmd_remote_mkfifo = self.ssh_cmd + ['mkfifo %s' % (self.remote_data_fifo,)] logger.info('ltacp %s: remote creating fifo. executing: %s' % (self.logId, ' '.join(cmd_remote_mkfifo))) p_remote_mkfifo = Popen(cmd_remote_mkfifo, stdout=PIPE, stderr=PIPE) @@ -233,41 +217,47 @@ class LtaCp: if p_remote_mkfifo.returncode != 0: raise LtacpException('ltacp %s: remote fifo creation failed: \nstdout: %s\nstderr: %s' % (self.logId, output_remote_mkfifo[0],output_remote_mkfifo[1])) - # get input datasize - cmd_remote_du = self.ssh_cmd + ['du -b --max-depth=0 %s' % (self.src_path_data,)] - logger.info('ltacp %s: remote getting datasize. executing: %s' % (self.logId, ' '.join(cmd_remote_du))) - p_remote_du = Popen(cmd_remote_du, stdout=PIPE, stderr=PIPE) - self.started_procs[p_remote_du] = cmd_remote_du - # block until du is finished - output_remote_du = p_remote_du.communicate() - del self.started_procs[p_remote_du] - if p_remote_du.returncode != 0: - raise LtacpException('ltacp %s: remote fifo creation failed: \nstdout: %s\nstderr: %s' % (self.logId, - output_remote_du[0], - output_remote_du[1])) - # compute various parameters for progress logging - input_datasize = int(output_remote_du[0].split()[0]) - logger.info('ltacp %s: input datasize: %d bytes, %s' % (self.logId, input_datasize, humanreadablesize(input_datasize))) - estimated_tar_size = 512*(input_datasize / 512) + 3*512 #512byte header, 2*512byte ending, 512byte modulo data - logger.info('ltacp %s: estimated_tar_size: %d bytes, %s' % (self.logId, estimated_tar_size, humanreadablesize(estimated_tar_size))) - tar_record_size = 10240 # 20 * 512 byte blocks + # get input filetype + cmd_remote_filetype = self.ssh_cmd + ['ls -l %s' % (self.src_path_data,)] + logger.info('ltacp %s: remote getting file info. executing: %s' % (self.logId, ' '.join(cmd_remote_filetype))) + p_remote_filetype = Popen(cmd_remote_filetype, stdout=PIPE, stderr=PIPE) + self.started_procs[p_remote_filetype] = cmd_remote_filetype + + # block until ls is finished + output_remote_filetype = p_remote_filetype.communicate() + del self.started_procs[p_remote_filetype] + if p_remote_filetype.returncode != 0: + raise LtacpException('ltacp %s: remote file listing failed: \nstdout: %s\nstderr: %s' % (self.logId, + output_remote_filetype[0], + output_remote_filetype[1])) + + # determine if input is file + input_is_file = (output_remote_filetype[0][0] == '-') + logger.info('ltacp %s: remote path is a %s' % (self.logId, 'file' if input_is_file else 'directory')) with open(os.devnull, 'r') as devnull: # start sending remote data, tee to fifo - src_path_parent, src_path_child = os.path.split(self.src_path_data) - cmd_remote_data = self.ssh_cmd + ['cd %s && tar c --blocking-factor=20 --checkpoint=1000 --checkpoint-action="ttyout=checkpoint %%u\\n" -O %s | tee %s | %s %s %s' % (src_path_parent, - src_path_child, - self.remote_data_fifo, - self.remoteNetCatCmd, - self.localIPAddress, - port_data)] + if input_is_file: + cmd_remote_data = self.ssh_cmd + ['cat %s | tee %s | %s %s %s' % (self.src_path_data, + self.remote_data_fifo, + self.remoteNetCatCmd, + self.localIPAddress, + port_data)] + else: + src_path_parent, src_path_child = os.path.split(self.src_path_data) + cmd_remote_data = self.ssh_cmd + ['cd %s && tar c -O %s | tee %s | %s %s %s' % (src_path_parent, + src_path_child, + self.remote_data_fifo, + self.remoteNetCatCmd, + self.localIPAddress, + port_data)] logger.info('ltacp %s: remote starting transfer. executing: %s' % (self.logId, ' '.join(cmd_remote_data))) p_remote_data = Popen(cmd_remote_data, stdin=devnull, stdout=PIPE, stderr=PIPE) self.started_procs[p_remote_data] = cmd_remote_data # start computation of checksum on remote fifo stream - cmd_remote_checksum = self.ssh_cmd + ['md5sum %s | %s %s %s' % (self.remote_data_fifo, self.remoteNetCatCmd, self.localIPAddress, port_md5)] + cmd_remote_checksum = self.ssh_cmd + ['md5sum %s' % (self.remote_data_fifo,)] logger.info('ltacp %s: remote starting computation of md5 checksum. executing: %s' % (self.logId, ' '.join(cmd_remote_checksum))) p_remote_checksum = Popen(cmd_remote_checksum, stdin=devnull, stdout=PIPE, stderr=PIPE) self.started_procs[p_remote_checksum] = cmd_remote_checksum @@ -277,7 +267,7 @@ class LtaCp: return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6) # waiting for output, comparing checksums, etc. - logger.info('ltacp %s: transfering...' % self.logId) + logger.info('ltacp %s: transfering... waiting for progress...' % self.logId) transfer_start_time = datetime.utcnow() prev_progress_time = datetime.utcnow() prev_bytes_transfered = 0 @@ -285,15 +275,19 @@ class LtaCp: # wait and poll for progress while all processes are runnning while len([p for p in self.started_procs.keys() if p.poll() is not None]) == 0: try: - # read and process tar stdout lines to create progress messages - nextline = p_remote_data.stdout.readline().strip() + current_progress_time = datetime.utcnow() + elapsed_secs_since_prev = timedelta_total_seconds(current_progress_time - prev_progress_time) + + if elapsed_secs_since_prev > 120: + raise LtacpException('ltacp %s: transfer stalled.' % (self.logId)) + + # read and process md5a32bc stdout lines to create progress messages + nextline = p_md5a32bc.stdout.readline().strip() + if len(nextline) > 0: - record_nr = int(nextline.split()[-1].strip()) - total_bytes_transfered = record_nr * tar_record_size + total_bytes_transfered = int(nextline.split()[0].strip()) percentage_done = (100.0*float(total_bytes_transfered))/float(estimated_tar_size) - current_progress_time = datetime.utcnow() elapsed_secs_since_start = timedelta_total_seconds(current_progress_time - transfer_start_time) - elapsed_secs_since_prev = timedelta_total_seconds(current_progress_time - prev_progress_time) if percentage_done > 0 and elapsed_secs_since_start > 0 and elapsed_secs_since_prev > 0: avg_speed = total_bytes_transfered / elapsed_secs_since_start current_bytes_transfered = total_bytes_transfered - prev_bytes_transfered @@ -303,16 +297,20 @@ class LtaCp: prev_bytes_transfered = total_bytes_transfered percentage_to_go = 100.0 - percentage_done time_to_go = elapsed_secs_since_start * percentage_to_go / percentage_done - logger.info('ltacp %s: transfered %d bytes, %s, %.1f%% at avgSpeed=%s curSpeed=%s to_go=%s' % (self.logId, - total_bytes_transfered, + logger.info('ltacp %s: transfered %s %.1f%% at avgSpeed=%s (%s) curSpeed=%s (%s) to_go=%s to %s' % (self.logId, humanreadablesize(total_bytes_transfered), percentage_done, humanreadablesize(avg_speed, 'Bps'), + humanreadablesize(avg_speed*8, 'bps'), humanreadablesize(current_speed, 'Bps'), - timedelta(seconds=int(round(time_to_go))))) + humanreadablesize(current_speed*8, 'bps'), + timedelta(seconds=int(round(time_to_go))), + dst_turl)) time.sleep(0.05) except KeyboardInterrupt: self.cleanup() + except Exception as e: + logger.error('ltacp %s: %s' % (self.logId, str(e))) logger.info('ltacp %s: waiting for transfer via globus-url-copy to LTA to finish...' % self.logId) output_data_out = p_data_out.communicate() @@ -332,42 +330,35 @@ class LtaCp: raise LtacpException('ltacp %s: Error in remote md5 checksum computation: %s' % (self.logId, output_remote_checksum[1])) logger.debug('ltacp %s: remote md5 checksum computation finished.' % self.logId) - logger.debug('ltacp %s: waiting to receive remote md5 checksum...' % self.logId) - output_md5_receive = p_md5_receive.communicate() - if p_md5_receive.returncode != 0: - raise LtacpException('ltacp %s: Error while receiving remote md5 checksum: %s' % (self.logId, output_md5_receive[1])) - logger.debug('ltacp %s: received md5 checksum.' % self.logId) - - logger.debug('ltacp %s: waiting for local computation of md5 checksum...' % self.logId) - output_md5_local = p_md5_local.communicate() - if p_md5_local.returncode != 0: - raise LtacpException('ltacp %s: Error while receiving remote md5 checksum: %s' % (self.logId, output_md5_local[1])) - logger.debug('ltacp %s: computed local md5 checksum.' % self.logId) - - # compare remote and local md5 checksums - try: - md5_checksum_remote = output_md5_receive[0].split(' ')[0] - md5_checksum_local = output_md5_local[0].split(' ')[0] + logger.info('ltacp %s: waiting for local computation of md5 adler32 and byte_count...' % self.logId) + output_md5a32bc_local = p_md5a32bc.communicate() + if p_md5a32bc.returncode != 0: + raise LtacpException('ltacp %s: Error while computing md5 adler32 and byte_count: %s' % (self.logId, output_md5a32bc_local[1])) + logger.debug('ltacp %s: computed local md5 adler32 and byte_count.' % self.logId) + + # process remote md5 checksums + try: + md5_checksum_remote = output_remote_checksum[0].split(' ')[0] + except Exception as e: + logger.error('ltacp %s: error while parsing remote md5: %s\n%s' % (self.logId, output_remote_checksum[0], output_remote_checksum[1])) + raise + + # process local md5 adler32 and byte_count + try: + items = output_md5a32bc_local[1].splitlines()[-1].split(' ') + md5_checksum_local = items[0].strip() + a32_checksum_local = items[1].strip().zfill(8) + byte_count = int(items[2].strip()) + except Exception as e: + logger.error('ltacp %s: error while parsing md5 adler32 and byte_count outputs: %s' % (self.logId, output_md5a32bc_local[0])) + raise + + logger.info('ltacp %s: byte count of datastream is %d %s' % (self.logId, byte_count, humanreadablesize(byte_count))) + + # compare local and remote md5 if(md5_checksum_remote != md5_checksum_local): raise LtacpException('md5 checksum reported by client (%s) does not match local checksum of incoming data stream (%s)' % (self.logId, md5_checksum_remote, md5_checksum_local)) logger.info('ltacp %s: remote and local md5 checksums are equal: %s' % (self.logId, md5_checksum_local,)) - except Exception as e: - logger.error('ltacp %s: error while parsing md5 checksum outputs: local=%s received=%s' % (self.logId, output_md5_local[0], output_md5_receive[0])) - raise - - logger.debug('ltacp %s: waiting for local byte count on datastream...' % self.logId) - output_byte_count = p_byte_count.communicate() - if p_byte_count.returncode != 0: - raise LtacpException('ltacp %s: Error while receiving remote md5 checksum: %s' % (self.logId, output_byte_count[1])) - byte_count = int(output_byte_count[0].split()[0].strip()) - logger.info('ltacp %s: byte count of datastream is %d %s' % (self.logId, byte_count, humanreadablesize(byte_count))) - - logger.debug('ltacp %s: waiting for local adler32 checksum to complete...' % self.logId) - output_a32_local = p_a32_local.communicate() - if p_a32_local.returncode != 0: - raise LtacpException('ltacp %s: local adler32 checksum computation failed: %s' (self.logId, str(output_a32_local))) - logger.debug('ltacp %s: finished computation of local adler32 checksum' % self.logId) - a32_checksum_local = output_a32_local[0].split()[1] logger.info('ltacp %s: fetching adler32 checksum from LTA...' % self.logId) srm_ok, srm_file_size, srm_a32_checksum = get_srm_size_and_a32_checksum(self.dst_surl) @@ -375,16 +366,17 @@ class LtaCp: if not srm_ok: raise LtacpException('ltacp %s: Could not get srm adler32 checksum for: %s' % (self.logId, self.dst_surl)) - if(srm_a32_checksum != a32_checksum_local): + if srm_a32_checksum != a32_checksum_local: raise LtacpException('ltacp %s: adler32 checksum reported by srm (%s) does not match original data checksum (%s)' % (self.logId, srm_a32_checksum, a32_checksum_local)) - logger.info('ltacp %s: adler32 checksums are equal: %s' % (self.logId, a32_checksum_local)) + logger.info('ltacp %s: adler32 checksums are equal: %s' % (self.logId, a32_checksum_local,)) - if(srm_file_size != byte_count): + if int(srm_file_size) != int(byte_count): raise LtacpException('ltacp %s: file size reported by srm (%s) does not match datastream byte count (%s)' % (self.logId, - srm_file_size, byte_count)) + srm_file_size, + byte_count)) logger.info('ltacp %s: srm file size and datastream byte count are equal: %s bytes (%s)' % (self.logId, srm_file_size, @@ -424,18 +416,25 @@ class LtaCp: return (p_listen, port) + def _removeRemoteFifo(self): + if hasattr(self, 'remote_data_fifo') and self.remote_data_fifo: + '''remove a file (or fifo) on a remote host. Test if file exists before deleting.''' + cmd_remote_ls = self.ssh_cmd + ['ls %s' % (self.remote_data_fifo,)] + p_remote_ls = Popen(cmd_remote_ls, stdout=PIPE, stderr=PIPE) + p_remote_ls.communicate() + + if p_remote_ls.returncode == 0: + cmd_remote_rm = self.ssh_cmd + ['rm %s' % (self.remote_data_fifo,)] + logger.info('ltacp %s: removing remote fifo. executing: %s' % (self.logId, ' '.join(cmd_remote_rm))) + p_remote_rm = Popen(cmd_remote_rm, stdout=PIPE, stderr=PIPE) + p_remote_rm.communicate() + if p_remote_rm.returncode != 0: + logger.error("Could not remove remote fifo %s@%s:%s\n%s" % (self.src_user, self.src_host, self.remote_data_fifo, p_remote_rm.stderr)) + def cleanup(self): logger.debug('ltacp %s: cleaning up' % (self.logId)) - if hasattr(self, 'remote_data_fifo') and self.remote_data_fifo: - '''remove a file (or fifo) on a remote host. Test if file exists before deleting.''' - cmd_remote_rm = self.ssh_cmd + ['if [ -e "%s" ] ; then rm %s ; fi ;' % (self.remote_data_fifo, self.remote_data_fifo)] - logger.info('ltacp %s: removing remote fifo. executing: %s' % (self.logId, ' '.join(cmd_remote_rm))) - p_remote_rm = Popen(cmd_remote_rm, stdout=PIPE, stderr=PIPE) - p_remote_rm.communicate() - if p_remote_rm.returncode != 0: - logger.error("Could not remove remote fifo %s@%s:%s\n%s" % (self.src_user, self.src_host, self.remote_data_fifo, p_remote_rm.stderr)) - self.remote_data_fifo = None + self._removeRemoteFifo() # remove local fifos for fifo in self.fifos: @@ -555,12 +554,18 @@ def create_missing_directories(surl): # limited standalone mode for testing: # usage: ltacp.py <remote-host> <remote-path> <surl> if __name__ == '__main__': + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + + # Check the invocation arguments + parser = OptionParser("%prog [options] <source_host> <source_path> <lta-detination-srm-url>", + description='copy a file/directory from <source_host>:<source_path> to the LTA <lta-detination-srm-url>') + parser.add_option("-u", "--user", dest="user", type="string", default=getpass.getuser(), help="username for to login on <host>, default: %s" % getpass.getuser()) + (options, args) = parser.parse_args() - if len(sys.argv) < 4: - print 'example: ./ltacp.py 10.196.232.11 /home/users/ingest/1M.txt srm://lofar-srm.fz-juelich.de:8443/pnfs/fz-juelich.de/data/lofar/ops/test/eor/1M.txt' - sys.exit() + if len(args) != 3: + parser.print_help() + sys.exit(1) - # transfer test: - cp = LtaCp(sys.argv[1], sys.argv[2], sys.argv[3], 'ingest') + cp = LtaCp(args[0], args[1], args[2], options.user) cp.transfer() diff --git a/LTA/LTAIngest/master.py b/LTA/LTAIngest/master.py index 773d5fe66f2338e9fb83ac2313d321597654d54b..55e2b5c1eed86ded94e95af66029fa93e6157a17 100755 --- a/LTA/LTAIngest/master.py +++ b/LTA/LTAIngest/master.py @@ -174,6 +174,7 @@ class jobHandler(Process): class manager(SyncManager): pass manager.register('number') manager.register('get') + manager.register('slave_names') self.manager = manager(address=(self.masterAddress, self.masterPort), authkey=self.masterAuth) self.manager.connect() nr_of_slaves = int(str(self.manager.number())) @@ -182,7 +183,7 @@ class jobHandler(Process): nr_of_slaves = int(str(self.manager.number())) time.sleep(10) #Let's wait a few seconds for any more slaves. Currently all slaves need to connect in 10 seconds. nr_of_slaves = int(str(self.manager.number())) - self.logger.info('Slaves found: %d' % nr_of_slaves) + self.logger.info('Slaves found: %d %s' % (nr_of_slaves, self.manager.slave_names())) os.system('echo "The LTA Ingest has been restarted."|mailx -s "LTA Ingest restarted" ' + self.mailCommand) ## ======= Main loop ====== @@ -204,7 +205,9 @@ class jobHandler(Process): self.update_job_msg.put((job, job['Status'], JobProducing, None)) job['Status'] = JobProducing self.active[job['ExportID']] = job - self.manager.get(None, job['destination']).put(job) ## sends it to the slave with the shortest queue of the possible destinations + job_slave_hosts = job.get('slave_hosts') + slave_for_job = self.manager.get(job_slave_hosts) + slave_for_job.put(job) ## sends it to the slave with the shortest queue of the possible slave_hosts self.logger.debug("Job's started: %s (%i)" % (job['ExportID'], len(self.active))) first = True except Empty: pass @@ -260,6 +263,12 @@ class queueHandler(Process): if job['Status'] == JobScheduled: self.update_job(job, None, JobScheduled, None) job['destination'] = self.job_groups[job['job_group']].get_destination() + + sourcehost = job['Location'].split(':')[0] + self.logger.info("job: %s sourcehost=%s" % (fileName, sourcehost)) + if 'cep4' in sourcehost.lower(): + job['slave_hosts'] = ['lexar003.offline.lofar', 'lexar004.offline.lofar'] + self.scheduled.put(job) # self.talker.put(job) ## Tell MoM we've done something else: @@ -353,27 +362,30 @@ class ltaMaster(): raise Exception('No MoM to listen to!') def add_slave(self, slave): + self.logger.info('Slave \'%s\' added to master' % (slave, )) self.slaves[slave] = Queue() return self.slaves[slave] def slave_size(self): return len(self.slaves) - ##Gives you the shortest slave queue unless you ask for a specific one. - def get_slave(self, source, destination): - if source: ## this code was developed for use on lse nodes/staging area, not really used. - return self.slaves[source] - else: - result = None - length = sys.maxint - for k in self.slaves.keys(): - if destination in k:# subselection of slaves based on destination, bit of a hack right now: choice between: lexar,lotar - size = self.slaves[k].qsize() - if length > size: - result = self.slaves[k] - length = size - self.logger.debug('found slave %s' % k) - return result + def slave_names(self): + return self.slaves.keys() + + ##Gives you the shortest slave queue out of the requested slave_hosts + def get_slave(self, slave_hosts): + if slave_hosts == None: + slave_hosts = self.slaves.keys() + + result = None + length = sys.maxint + for k in slave_hosts: + size = self.slaves[k].qsize() + if length > size: + result = self.slaves[k] + length = size + self.logger.debug('found slave %s' % k) + return result def remove_slave(self, slave): q = self.slaves.pop(slave, None) @@ -391,6 +403,7 @@ class ltaMaster(): class manager(SyncManager): pass manager.register('add_slave', self.add_slave) manager.register('number', self.slave_size) + manager.register('slave_names', self.slave_names) manager.register('get', self.get_slave) manager.register('remove_slave', self.remove_slave) manager.register('slave_done', self.slave_done) diff --git a/LTA/LTAIngest/md5adler/makefile b/LTA/LTAIngest/md5adler/makefile index 1881dac90294337979667819652efac72b55ea0a..6768b3463664bab06403c3cdc06dc14b910fe64d 100644 --- a/LTA/LTAIngest/md5adler/makefile +++ b/LTA/LTAIngest/md5adler/makefile @@ -2,17 +2,21 @@ CFLAGS=-O3 -all: md5a32 a32 +all: md5a32 a32 md5a32bc md5a32: md5a32.o adler32.o md5a32.o: md5.h +md5a32bc: md5a32bc.o adler32.o + +md5a32bc.o: md5.h + adler32: adler32.c adler32.h gcc -c adler32.c -o adler32.o a32: a32.o adler32.o clean: - rm -f adler32.o md5a32.o a32.o md5a32 a32 + rm -f adler32.o md5a32.o md5a32bc.o a32.o md5a32 md5a32bc a32 diff --git a/LTA/LTAIngest/md5adler/md5.h b/LTA/LTAIngest/md5adler/md5.h index a45ba7ae955b4dc3f444389aba21165ccb804ae4..d8f4dab91cc12adc3fb8266bafebed967a1838df 100644 --- a/LTA/LTAIngest/md5adler/md5.h +++ b/LTA/LTAIngest/md5adler/md5.h @@ -42,9 +42,12 @@ /* typedef unsigned long int UINT4; */ typedef unsigned int UINT4; -/* Data structure for MD5 (Message Digest) computation */ +/* Data structure for MD5 (Message Digest) computation + * plus additional adler32 and byte_count variables + */ typedef struct { unsigned int adler32; /* adler 32 crc */ + unsigned long byte_count; /* total number of bytes processed */ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ UINT4 buf[4]; /* scratch buffer */ unsigned char in[64]; /* input buffer */ diff --git a/LTA/LTAIngest/md5adler/md5a32bc b/LTA/LTAIngest/md5adler/md5a32bc new file mode 100755 index 0000000000000000000000000000000000000000..64d6aa9eb8597fa9042461796da9082f6835d73d Binary files /dev/null and b/LTA/LTAIngest/md5adler/md5a32bc differ diff --git a/LTA/LTAIngest/md5adler/md5a32bc.c b/LTA/LTAIngest/md5adler/md5a32bc.c new file mode 100644 index 0000000000000000000000000000000000000000..fc4573012c3161cd5a53b51b51dc17f1d2ac5243 --- /dev/null +++ b/LTA/LTAIngest/md5adler/md5a32bc.c @@ -0,0 +1,376 @@ +/* + ********************************************************************** + ** md5a32bc.c ** + ** Compute md5, adler32 and byte count while transfering data from ** + ** stdin to stdout. Results are printed on stderr. ** + ** ** + ** md5 methods are copied from ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +#include <stdio.h> +#include "md5.h" + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (mdContext) +MD5_CTX *mdContext; +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; + + /* Set adler32 init value to one + */ + mdContext->adler32 = 1; + + mdContext->byte_count = 0; +} + +void MD5Update (mdContext, inBuf, inLen) +MD5_CTX *mdContext; +unsigned char *inBuf; +unsigned int inLen; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } + +} + +void MD5Final (mdContext) +MD5_CTX *mdContext; +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#include <stdio.h> + +int main (int argc, char *argv[]) +{ + if (argc == 2 && strcmp (argv[1], "-h") == 0) + { + printf("md5a32bc is a tool which computes the md5 and adler32 checksum and counts the number of bytes on the stdin datastream.\n"); + printf("this input datastream is copied and written to stdout by default, or to the output file given as last argument.\n"); + printf("progress messages can be written every <n> bytes with flag -p <n>.\n"); + printf("\n"); + printf("Usage:\n"); + printf("<some_prog> | md5a32bc\n"); + printf("or:\n"); + printf("<some_prog> | md5a32bc <my_output_file>\n"); + printf("or:\n"); + printf("<some_prog> | md5a32bc -p <n> <my_output_file>\n"); + printf("or:\n"); + printf("<some_prog> | md5a32bc -p <n>\n"); + exit(0); + } + + MD5_CTX mdContext; + int num_bytes_read, num_bytes_written; + const int BUFFSIZE = 4096; + unsigned char data[BUFFSIZE]; + int num_progress_bytes = -1; + int progress_step_cntr = 0; + int progress_step = 0; + + if (argc >= 3 && strcmp (argv[1], "-p") == 0) + { + int num = atoi(argv[2]); + if(num > 0) + num_progress_bytes = num; + } + + FILE *outfile = stdout; + FILE *logstream = stderr; + FILE *resultstream = stderr; + + if (argc == 2 || argc == 4) + { + outfile = fopen(argv[argc-1], "wb"); + logstream = stdout; + } + + MD5Init (&mdContext); + + while (1) + { + num_bytes_read = fread (data, 1, BUFFSIZE, stdin); + + if(num_bytes_read == 0) + break; + + mdContext.adler32=adler32(mdContext.adler32, data, num_bytes_read); + MD5Update (&mdContext, data, num_bytes_read); + mdContext.byte_count += num_bytes_read; + + num_bytes_written = fwrite (data, 1, num_bytes_read, outfile); + + if(num_bytes_written != num_bytes_read) + { + fprintf (logstream, "error while writing\n"); + if(outfile != stdout) + fclose(outfile); + return -1; + } + + progress_step = mdContext.byte_count / num_progress_bytes; + + if(progress_step > progress_step_cntr) + { + fprintf (logstream, "%lu bytes processed\n", mdContext.byte_count); + fflush(logstream); + progress_step_cntr = progress_step; + } + } + + fflush(outfile); + fflush(logstream); + + if(outfile != stdout) + fclose(outfile); + + MD5Final (&mdContext); + + for (int i = 0; i < 16; i++) + fprintf (resultstream, "%02x", mdContext.digest[i]); + + fprintf (resultstream, " %x %lu\n", mdContext.adler32, mdContext.byte_count); + fflush(resultstream); + + return 0; +} diff --git a/LTA/LTAIngest/slave.py b/LTA/LTAIngest/slave.py index 680a77afadbed5543630f48ee8393bb37ac35534..0b2ae2921a4b117bad0d7ff17958b02e32a489f3 100755 --- a/LTA/LTAIngest/slave.py +++ b/LTA/LTAIngest/slave.py @@ -135,8 +135,10 @@ class executer(Process): if (self.job['Status'] == JobProduced) or (self.job['Status'] == JobError): self.talker.put(self.job) self.manager.slave_done(self.job, self.result, pipeline.FileType) - with self.jobs.get_lock(): - self.jobs.value -= 1 + #python 'with' does not work for some reason, so just use acquire/release + self.jobs.get_lock().acquire() + self.jobs.value -= 1 + self.jobs.get_lock().release() self.logger.debug("Slave Pipeline executer finished for %s in %d sec" % (self.job['ExportID'], time.time() - start)) ## ---------------- LTA Slave -------------------------------------------- @@ -198,8 +200,10 @@ class ltaSlave(): except QueueEmpty: job = None if job: - with self.jobs.get_lock(): - self.jobs.value += 1 + #python 'with' does not work for some reason, so just use acquire/release + self.jobs.get_lock().acquire() + self.jobs.value += 1 + self.jobs.get_lock().release() runner = executer(self.logger, self.logdir, job, talker, self.jobs, self.momClient, self.ltaClient, self.host, self.ltacpport, self.mailSlCommand, self.manager, self.pipelineRetry, self.momRetry, self.ltaRetry, self.srmRetry, self.srmInit) runner.start() else: diff --git a/LTA/ltastorageoverview/test/test_lso_webservice.py b/LTA/ltastorageoverview/test/test_lso_webservice.py index 6cc2e2e25275555a0246f2dbc32e7cd109a2dfdb..4831f3bec65e4c025873d10ff7fdb2368aefc906 100755 --- a/LTA/ltastorageoverview/test/test_lso_webservice.py +++ b/LTA/ltastorageoverview/test/test_lso_webservice.py @@ -117,7 +117,7 @@ class TestLTAStorageWebService(FlaskLiveTestCase): def main(argv): - unittest.main(verbosity=2) + unittest.main() # run tests if main if __name__ == '__main__': diff --git a/LTA/ltastorageoverview/test/test_store.py b/LTA/ltastorageoverview/test/test_store.py index d95c318f37c821c136d8457e337b203c237ccef6..30b08fa0325956534ad5ca7da396c61c888f55ad 100755 --- a/LTA/ltastorageoverview/test/test_store.py +++ b/LTA/ltastorageoverview/test/test_store.py @@ -128,4 +128,4 @@ class TestLTAStorageDb(unittest.TestCase): # run tests if main if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main() diff --git a/MAC/APL/APLCommon/src/swlevel b/MAC/APL/APLCommon/src/swlevel index 6ec96f8081ef0e2577355bb481e374f08c0f253d..24849dd71ddf4533a71953e0b1dc1d339ced6e05 100755 --- a/MAC/APL/APLCommon/src/swlevel +++ b/MAC/APL/APLCommon/src/swlevel @@ -26,8 +26,8 @@ # if [ "$LOFARROOT" == "" ]; then - # default value until all MAC controlled systems provide $LOFARROOT - LOFARROOT=/opt/lofar + # default value until all MAC controlled systems provide $LOFARROOT + LOFARROOT=/opt/lofar fi BINDIR=$LOFARROOT/bin @@ -36,7 +36,7 @@ LOGDIR=$LOFARROOT/var/log ETCDIR=$LOFARROOT/etc LEVELTABLE=${ETCDIR}/swlevel.conf -# Make sure all files are user/group writeable (needed for Int. +# Make sure all files are user/group writeable (needed for Int. # Stations) umask 002 @@ -56,10 +56,10 @@ SyntaxError() echo "-v: Show running LOFAR version, exit (-V: only print version)" echo "-u: Show users owning running processes (-U: same)" echo "-i: Load RSP firmware from image [x] (default image is 1)" - echo "-l: Set to level as provided (optional)" - echo "-q: Quit process with name processname" - echo "-r: Run process with name processname" - echo + echo "-l: Set to level as provided (optional)" + echo "-q: Quit process with name processname" + echo "-r: Run process with name processname" + echo echo "Levels:" echo "0: Stop all lofar software" echo "1: Run Lofar daemons and PVSS" @@ -76,34 +76,34 @@ SyntaxError() # First argument is timeout, next arguments are the command and its parameters -# Find which image to load on a given station; uses file +# Find which image to load on a given station; uses file # ${ETCDIR}/RSPImage.conf # # returns $image with image number findImage() -{ - findstation=$1 - RSPImageFile=${ETCDIR}/RSPImage.conf - if [ ! -e $RSPImageFile ] ; then - echo "Cannot find Image file ${ETCDIR}/RSPImage.conf" - exit; - fi - image=0 - while read line - do - first=`echo $line | awk '{print $1}'` - if [ "$first" == "$findstation" ]; then - image=`echo $line | awk '{print $2}'` - break - fi - done < ${ETCDIR}/RSPImage.conf - if [ $image -eq 0 ]; then - echo "Could not find station $findstation in file ${ETCDIR}/RSPImage.conf" - exit; - fi - return -} +{ + findstation=$1 + RSPImageFile=${ETCDIR}/RSPImage.conf + if [ ! -e $RSPImageFile ] ; then + echo "Cannot find Image file ${ETCDIR}/RSPImage.conf" + exit; + fi + image=0 + while read line + do + first=`echo $line | awk '{print $1}'` + if [ "$first" == "$findstation" ]; then + image=`echo $line | awk '{print $2}'` + break + fi + done < ${ETCDIR}/RSPImage.conf + if [ $image -eq 0 ]; then + echo "Could not find station $findstation in file ${ETCDIR}/RSPImage.conf" + exit; + fi + return +} # # selectImage(); load an image on the RSP boards @@ -111,45 +111,36 @@ findImage() selectImage() { let nrRSPs=`grep RSPBOARDS ${ETCDIR}/RemoteStation.conf | cut -d'=' -f2 | sed 's/ //g'` - let offset=0x`grep RSPDriver.MAC_ADDR_0 ${ETCDIR}/RSPDriver.conf | cut -d'=' -f2 | cut -d':' -f5` + let offset=0x`grep RSPDriver.MAC_ADDR_0 ${ETCDIR}/RSPDriver.conf | cut -d'=' -f2 | cut -d':' -f5` let board=0 - # Assume no errors with board communication + # Assume no errors with board communication boardError=0 - errorBoards="" + errorBoards="" # Make sure we have an image number in parameter $image - if [ -z $image ] && [ $imageForced -eq 0 ]; then - findImage `hostname -s` - fi + if [ -z $image ] && [ $imageForced -eq 0 ]; then + findImage `hostname -s` + fi while [ $board -lt $nrRSPs ] do # get major version of active firmware on RSPboard $board - #boardHex=`echo $board | awk '{ printf "%02x", $1 }'` + #boardHex=`echo $board | awk '{ printf "%02x", $1 }'` boardHex=`echo $board | awk -v firstBoard=$offset '{ printf "%02x", firstBoard+$1 }'` # Uncomment next lines only for testing purposes! - #if [ "$boardHex" == "03" ]; then + #if [ "$boardHex" == "03" ]; then # boardHex="1F" - #fi - rsu=`run_timeout.sh 5 sudo ${SBINDIR}/rsuctl3 -m 10:fa:00:00:$boardHex:00 -qV 2>&1 | grep BP | cut -d':' -f2 | sed 's/ //g' | cut -d'.' -f1` - - # If not a single number, something weird must have happened - if [ ${#rsu} -ne 1 ]; then - echo "RSPboard $boardHex: Error requesting active firmware version (communication error)" - boardError=1 - errorBoards=${errorBoards}${boardHex}"," - else - echo "Loading image $image on RSPboard $boardHex ..." - run_timeout.sh 5 sudo ${SBINDIR}/rsuctl3_reset -m 10:fa:00:00:$boardHex:00 -q -x -p $image 1>/dev/null 2>&1 - if [ $? -ne 0 ]; then - boardError=1 - errorBoards=${errorBoards}${boardHex}"," - fi - fi - # Next board + #fi + echo "Loading image $image on RSPboard $boardHex ..." + run_timeout.sh 5 sudo ${SBINDIR}/rsuctl3_reset -m 10:fa:00:00:$boardHex:00 -q -x -p $image 1>/dev/null 2>&1 + if [ $? -ne 0 ]; then + boardError=1 + errorBoards=${errorBoards}${boardHex}"," + fi let board+=1 done + if [ $boardError -eq 1 ]; then - echo "Board(s) $errorBoards have a communication problem; try reset the 48V" + echo "Board(s) $errorBoards have a communication problem; try reset the 48V" fi } @@ -159,58 +150,58 @@ selectImage() # check_images() { -if [ -e $BINDIR/rspctl ]; then - # First make sure RSP images are properly loaded - # Introduce a timeout of 60 sec for images to initialize - echo "Waiting for RSP and TBB images to be initialized" - timeout=60 - for (( s=0 ; s<timeout; s++ )) - do - rsu_ready=`( rspctl --version | grep "0.0" ) >& /dev/null; echo $?` - if [ $rsu_ready == 1 ]; then - echo "RSP Images are loaded" - break - fi - sleep 1 - done - if [ $s == $timeout ]; then - echo "Could not load RSP images in time; Reset RSP boards" - exit 1 - fi +if [ -e $BINDIR/rspctl ]; then + # First make sure RSP images are properly loaded + # Introduce a timeout of 60 sec for images to initialize + echo "Waiting for RSP and TBB images to be initialized" + timeout=60 + for (( s=0 ; s<timeout; s++ )) + do + rsu_ready=`( rspctl --version | grep "0.0" ) >& /dev/null; echo $?` + if [ $rsu_ready == 1 ]; then + echo "RSP Images are loaded" + break + fi + sleep 1 + done + if [ $s == $timeout ]; then + echo "Could not load RSP images in time; Reset RSP boards" + exit 1 + fi fi if [ -e $BINDIR/tbbctl ]; then - # Now make sure TBB images are properly loaded - # Introduce a timeout of 60 sec for images to initialize - echo "Waiting for TBB images to be initialized" - timeout=60 - sleep 10 - for (( s=0 ; s<timeout; s++ )) - do - tbb_respons=`tbbctl --version` - tbb_ready=`( echo $tbb_respons | grep "\ V\ " ) >& /dev/null; echo $?` - if [ $tbb_ready -eq 0 ]; then - sleep 10 # additional delay to let TBB boards end their init phase - echo "TBB Images are loaded" - break - fi - tbb_down=`( echo $tbb_respons | grep "TBBDriver is NOT responding" )>& /dev/null; echo $?` - if [ $tbb_down -eq 0 ]; then - echo "TBBDriver is not responding; cannot continue start of TBBs" - # Trigger message furtheron in the code - s=$timeout - break - fi - sleep 1 - done - if [ $s == $timeout ]; then - echo "Could not load TBB images; Reset TBB boards" - else - # Start TBB recording mode - if [ -e $SBINDIR/startTBB.sh ]; then - $SBINDIR/startTBB.sh - fi - fi + # Now make sure TBB images are properly loaded + # Introduce a timeout of 60 sec for images to initialize + echo "Waiting for TBB images to be initialized" + timeout=60 + sleep 10 + for (( s=0 ; s<timeout; s++ )) + do + tbb_respons=`tbbctl --version` + tbb_ready=`( echo $tbb_respons | grep "\ V\ " ) >& /dev/null; echo $?` + if [ $tbb_ready -eq 0 ]; then + sleep 10 # additional delay to let TBB boards end their init phase + echo "TBB Images are loaded" + break + fi + tbb_down=`( echo $tbb_respons | grep "TBBDriver is NOT responding" )>& /dev/null; echo $?` + if [ $tbb_down -eq 0 ]; then + echo "TBBDriver is not responding; cannot continue start of TBBs" + # Trigger message furtheron in the code + s=$timeout + break + fi + sleep 1 + done + if [ $s == $timeout ]; then + echo "Could not load TBB images; Reset TBB boards" + else + # Start TBB recording mode + if [ -e $SBINDIR/startTBB.sh ]; then + $SBINDIR/startTBB.sh + fi + fi fi } @@ -226,7 +217,7 @@ start_prog() # special check for logging-daemons [ $prog == $logProgToSkip ] && return - + # check existance [ -x $BINDIR/$prog ] || [ -x $BINDIR/${prog}.sh ] || return @@ -241,49 +232,48 @@ start_prog() if [ $? -ne 0 ]; then curdate=`date +%Y%m%dT%H%M%S` # PVSS needs special treatment - if [ "$prog" = "PVSS00pmon" ]; then - echo Starting $prog - start_pvss2 1>/dev/null 2>&1 & - sleep 3 - elif [ "$prog" = "SASGateway" ]; then - # Foreign stations not under central control should not - # connect to the SAS database; this prevents SAS main- - # tenance etc. - if [ "$user" = "lofarsys" ]; then + if [ "$prog" = "PVSS00pmon" ]; then echo Starting $prog - rm -f $LOGDIR/$prog.log*.? 1>/dev/null 2>&1 - $BINDIR/$prog 1>>${LOGDIR}/${prog}.stdout.${curdate} 2>&1 & - else - echo "Local use, not starting $prog" - fi - else - if [ -n "$asroot" ]; then - echo Starting $prog - sudo rm -f $LOGDIR/$prog.log.? 1>/dev/null 2>&1 - if [ "$prog" = "RSPDriver" ]; then - selectImage - if [ $boardError -eq 1 ]; then - exit - fi - fi - if [ "$prog" = "TBBDriver" ]; then - # Check if RSPDriver is already running; if not, do not start either! - /sbin/pidof RSPDriver 1>/dev/null 2>&1 - if [ $? -ne 0 ]; then - echo "RSPDriver not running, so not starting TBBDriver either" - exit - fi - fi - sudo -b $BINDIR/$prog 1>>$LOGDIR/$prog.stdout.${curdate} 2>&1 - if [ "$prog" = "TBBDriver" ]; then - check_images - fi - else - - echo Starting $prog - rm -f $LOGDIR/$prog.log*.? 1>/dev/null 2>&1 - $BINDIR/$prog 1>>$LOGDIR/$prog.stdout.${curdate} 2>&1 & - fi + start_pvss2 1>/dev/null 2>&1 & + sleep 3 + elif [ "$prog" = "SASGateway" ]; then + # Foreign stations not under central control should not + # connect to the SAS database; this prevents SAS main- + # tenance etc. + if [ "$user" = "lofarsys" ]; then + echo Starting $prog + rm -f $LOGDIR/$prog.log*.? 1>/dev/null 2>&1 + $BINDIR/$prog 1>>${LOGDIR}/${prog}.stdout.${curdate} 2>&1 & + else + echo "Local use, not starting $prog" + fi + else + if [ -n "$asroot" ]; then + echo Starting $prog + sudo rm -f $LOGDIR/$prog.log.? 1>/dev/null 2>&1 + if [ "$prog" = "RSPDriver" ]; then + selectImage + if [ $boardError -eq 1 ]; then + exit + fi + fi + if [ "$prog" = "TBBDriver" ]; then + # Check if RSPDriver is already running; if not, do not start either! + /sbin/pidof RSPDriver 1>/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "RSPDriver not running, so not starting TBBDriver either" + exit + fi + fi + sudo -b $BINDIR/$prog 1>>$LOGDIR/$prog.stdout.${curdate} 2>&1 + if [ "$prog" = "TBBDriver" ]; then + check_images + fi + else + echo Starting $prog + rm -f $LOGDIR/$prog.log*.? 1>/dev/null 2>&1 + $BINDIR/$prog 1>>$LOGDIR/$prog.stdout.${curdate} 2>&1 & + fi fi usleep 250000 ps -ef | grep -v grep | egrep '[0-9][0-9] [a-zA-Z0-9/_.]*/'${prog} @@ -299,14 +289,14 @@ stop_prog() prog=$1 asroot=${2:1} withmpi=${3:1} - [ ! -z "$asroot" ] && asroot=sudo + [ ! -z "$asroot" ] && asroot=sudo # special check for logging-daemons [ $prog == $logProgToSkip ] && return - + # check existance [ -x $BINDIR/$prog ] || [ -x $BINDIR/${prog}.sh ] || return - + # if it is a shell script call the script if [ -f $BINDIR/${prog}.sh ]; then $BINDIR/${prog}.sh stop @@ -319,6 +309,14 @@ stop_prog() return fi + # if RSPDriver disable external clock first (use 125MHz board clock) + if [ "$prog" = "RSPDriver" ]; then + echo "disable clock output on clock board" + rspctl --clock=0 1>/dev/null 2>&1 + # wait while setting is done + sleep 15 + fi + # PVSS needs special treatment if [ "$prog" = "PVSS00pmon" ]; then echo "Stopping PVSS database" @@ -334,7 +332,7 @@ stop_prog() # first try normal kill for pid in `/sbin/pidof -x ${prog}` - do + do echo "Softly killing ${prog}(${pid})" $asroot kill $pid 1>/dev/null 2>&1 usleep 500000 @@ -342,41 +340,52 @@ stop_prog() # when normal kill did not work, kill is with -9 for pid in `/sbin/pidof -x ${prog}` - do + do echo "Hard killing ${prog}(${pid})" $asroot kill -9 $pid 1>/dev/null 2>&1 usleep 500000 done - # if user0 or lofarsys, try normal kill as root - - for pid in `/sbin/pidof -x ${prog}` - do - if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then - sudo kill $pid 1>/dev/null 2>&1 - usleep 50000 - fi - done - - # if user0 or lofarsys, try hard kill as root - for pid in `/sbin/pidof -x ${prog}` - do - if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then - sudo kill -9 $pid 1>/dev/null 2>&1 - usleep 50000 - fi - done - - # if still alive, write a message - for pid in `/sbin/pidof -x ${prog}` - do - echo -n "Could not kill ${prog}(${pid}); " - if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then - echo "tried it as root as well, giving up..." - else - echo "probably run by another user, contact your system administrator" - fi + # if user0 or lofarsys, try normal kill as root + + for pid in `/sbin/pidof -x ${prog}` + do + if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then + sudo kill $pid 1>/dev/null 2>&1 + usleep 50000 + fi done + # if user0 or lofarsys, try hard kill as root + for pid in `/sbin/pidof -x ${prog}` + do + if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then + sudo kill -9 $pid 1>/dev/null 2>&1 + usleep 50000 + fi + done + + # if still alive, write a message + for pid in `/sbin/pidof -x ${prog}` + do + echo -n "Could not kill ${prog}(${pid}); " + if [ "$user" == "user0" -o "$user" == "lofarsys" ]; then + echo "tried it as root as well, giving up..." + else + echo "probably run by another user, contact your system administrator" + fi + done + + # if RSPDriver set rsp firmware to image 0 (factory image) + if [ "$prog" = "RSPDriver" ]; then + image=0 + imageForced=1 + selectImage + if [ $boardError -eq 1 ]; then + exit + fi + echo "RSP image set to image 0, wait.." + sleep 12 + fi } # @@ -391,17 +400,17 @@ status_prog() list=( `cat $LEVELTABLE | cut -d"#" -f1 | awk '{ if (NF>0) print $0 }' ` ) for line in ${list[@]} do - # expected process and swlevel it should run in + # expected process and swlevel it should run in levelnr=`echo $line | cut -d":" -f1` prog=`echo $line | cut -d":" -f6` - pid=("") + pid=("") # special check for logging-daemons [ $prog == $logProgToSkip ] && continue - + # check existance [ -x $BINDIR/$prog ] || [ -x $BINDIR/${prog}.sh ] || continue - + if [ $prevlevel -ne $levelnr ]; then echo "---" prevlevel=$levelnr @@ -422,11 +431,11 @@ status_prog() i=0 for apid in ${pid[@]} do - obsid[i]=`ps -p $apid --no-heading -o command | awk -F{ '{print $2}' | awk -F} '{print $1}'` - if [ $show_users -eq 1 ]; then - pid_user[i]=`ps -p $apid -o user=` - fi - i=$i+1 + obsid[i]=`ps -p $apid --no-heading -o command | awk -F{ '{print $2}' | awk -F} '{print $1}'` + if [ $show_users -eq 1 ]; then + pid_user[i]=`ps -p $apid -o user=` + fi + i=$i+1 done # If a program is running in a level higher than the level # that should be active, raise the active level to indicate @@ -436,47 +445,47 @@ status_prog() pid="DOWN" fi - if [ "$pid" != "DOWN" ] && [ ${#obsid[0]} != 0 ]; then - echo ${levelnr}:${prog}:${pid[*]}:${obsid[*]} | awk -F: '{ printf "%s : %-25s %s [ObsID: %s]\n", $1, $2, $3, $4 }' + if [ "$pid" != "DOWN" ] && [ ${#obsid[0]} != 0 ]; then + echo ${levelnr}:${prog}:${pid[*]}:${obsid[*]} | awk -F: '{ printf "%s : %-25s %s [ObsID: %s]\n", $1, $2, $3, $4 }' elif [ "$pid" != "DOWN" ] && [ ${show_users} -eq 1 ]; then - echo ${levelnr}:${prog}:${pid[*]}:${pid_user[*]} | awk -F: '{ printf "%s : %-25s %s [%s]\n", $1, $2, $3, $4 }' + echo ${levelnr}:${prog}:${pid[*]}:${pid_user[*]} | awk -F: '{ printf "%s : %-25s %s [%s]\n", $1, $2, $3, $4 }' else - echo ${levelnr}:${prog}:${pid[*]} | awk -F: '{ printf "%s : %-25s %s\n", $1, $2, $3}' + echo ${levelnr}:${prog}:${pid[*]} | awk -F: '{ printf "%s : %-25s %s\n", $1, $2, $3}' fi # Some Checks # Controllers must have one instance, only. Some programs may have more instances. - if [ ${#pid[@]} -ge 2 ]; then - if [ "$prog" != "ObservationControl" \ - -a "$prog" != "PythonControl" \ - -a "$prog" != "OnlineControl" ]; then - toomany="$toomany ${prog}[$levelnr]" - fi + if [ ${#pid[@]} -ge 2 ]; then + if [ "$prog" != "ObservationControl" \ + -a "$prog" != "PythonControl" \ + -a "$prog" != "OnlineControl" ]; then + toomany="$toomany ${prog}[$levelnr]" + fi fi - - # Check for missing controllers - if [ "$pid" = "DOWN" -o "$pid" = "0" ]; then - if [ $levelnr -le $level ]; then - if [ $levelnr -le 5 ]; then - missing="$missing ${prog}[$levelnr]" - else - # LCU level 6 has two permanent controllers running - if [ "$prog" == "StationControl" \ - -o "$prog" == "ClockControl" ]; then - missing="$missing ${prog}[$levelnr]" - fi - # MCU level 6 must have MACScheduler running - if [ "$prog" == "MACScheduler" ]; then - missing="$missing ${prog}[$levelnr]" + + # Check for missing controllers + if [ "$pid" = "DOWN" -o "$pid" = "0" ]; then + if [ $levelnr -le $level ]; then + if [ $levelnr -le 5 ]; then + missing="$missing ${prog}[$levelnr]" + else + # LCU level 6 has two permanent controllers running + if [ "$prog" == "StationControl" \ + -o "$prog" == "ClockControl" ]; then + missing="$missing ${prog}[$levelnr]" + fi + # MCU level 6 must have MACScheduler running + if [ "$prog" == "MACScheduler" ]; then + missing="$missing ${prog}[$levelnr]" + fi + fi fi - fi - fi fi done echo "---" - if [ "$missing" ]; then + if [ "$missing" ]; then echo "Missing :"$missing fi - if [ "$toomany" ]; then + if [ "$toomany" ]; then echo "Too many:"$toomany fi } @@ -488,210 +497,217 @@ goto_level() { #first power down to new level newlevel=$1 - + if [ -e /tmp/level.admin ]; then + curlevel=`cat /tmp/level.admin` + else + curlevel=0 + fi # set rcumode to 0 (power save) when entering level 1 if [ ${newlevel} -le 1 ]; then - if [ -e /tmp/level.admin ]; then - curlevel=`cat /tmp/level.admin` - else - curlevel=-1 - fi - if [ ${curlevel} -ge 2 ]; then - /sbin/pidof RSPDriver 1>/dev/null 2>&1 - if [ $? -eq 0 ]; then - status=`( rspctl --version | grep "0.0" ) >& /dev/null; echo $?` - if [ $status == 1 ]; then - echo "set rcumode to 0 (power save)" - rspctl --rcumode=0 1>/dev/null 2>&1 - # Wait for setting to take effect before killing RSPDriver - sleep 2 - else + if [ ${curlevel} -ge 2 ]; then + /sbin/pidof RSPDriver 1>/dev/null 2>&1 + if [ $? -eq 0 ]; then + status=`( rspctl --version | grep "0.0" ) >& /dev/null; echo $?` + if [ $status == 1 ]; then + echo "set rcumode to 0 (power save)" + rspctl --rcumode=0 1>/dev/null 2>&1 + # Wait for setting to take effect before killing RSPDriver + sleep 2 + else echo "Beware: NOT going to rcumode 0 as images are still being initialized" - fi - fi - if [ -e $SBINDIR/stopTBB.sh ]; then - echo "Stopping TBB recording mode" - $SBINDIR/stopTBB.sh - fi - fi + fi + fi + if [ -e $SBINDIR/stopTBB.sh ]; then + echo "Stopping TBB recording mode" + $SBINDIR/stopTBB.sh + fi + fi fi - for (( l=6 ; l>newlevel ; l-- )) + for (( l=6 ; l > newlevel ; l-- )) do - tac $LEVELTABLE | cut -d"#" -f1 | awk '{ if (NF>0) print $0 }' | \ + if [ $curlevel -gt $l ]; then + # echo "Write new swlevel $l to /tmp/level.admin" + echo $l > /tmp/level.admin + fi + tac $LEVELTABLE | cut -d"#" -f1 | awk '{ if (NF>0) print $0 }' | \ grep "^${l}:" | grep ":d:" | while read line do - ( + ( asroot=`echo $line | cut -d":" -f4` withmpi=`echo $line | cut -d":" -f5` program=`echo $line | cut -d":" -f6` stop_prog $program x$asroot x$withmpi - ) <&- # cant have programs reading from stdin - # as that would mess up 'while read line' + ) <&- # cant have programs reading from stdin + # as that would mess up 'while read line' done + done - # then power up to new level - for (( l=1 ; l<=newlevel ; l++ )) + # then power up to new level + for (( l=1 ; l <= newlevel ; l++ )) do - - # Start programs for level $l - cat $LEVELTABLE | cut -d"#" -f1 | awk '{ if (NF>0) print $0 }' | grep "^${l}:" | grep ":u:" | while read line + # Start programs for level $l + cat $LEVELTABLE | cut -d"#" -f1 | awk '{ if (NF>0) print $0 }' | grep "^${l}:" | grep ":u:" | while read line do - ( + ( asroot=`echo $line | cut -d":" -f4` withmpi=`echo $line | cut -d":" -f5` program=`echo $line | cut -d":" -f6` start_prog $program x$asroot x$withmpi - ) <&- # cant have programs reading from stdin - # as that would mess up 'while read line' - done + ) <&- # cant have programs reading from stdin + # as that would mess up 'while read line' + done + if [ $curlevel -le $l ]; then + # echo "Write new swlevel $l to /tmp/level.admin" + echo $l > /tmp/level.admin + fi done } show_level() { - if [ -e /tmp/level.admin ]; then - level=`cat /tmp/level.admin` - status_prog >& /dev/null - if [ $highest_level_running -gt $level ]; then - level=$highest_level_running - status_prog >& /dev/null - fi - if [ "$missing" != "" ]; then - let level=0-$level - fi - - if [ "$1" != "S" ]; then - echo -n "Currently set level is " - fi - echo $level - if [ "$1" = "S" -o "$1" = "s" ]; then - exit - fi - else - level=-1 - if [ "$1" != "S" ]; then - echo "Currently set level unknown" - fi - fi - # argument -s/-S only returns level, no list - if [ -z "$1" ]; then - status_prog - fi - exit $level + if [ -e /tmp/level.admin ]; then + level=`cat /tmp/level.admin` + status_prog >& /dev/null + if [ $highest_level_running -gt $level ]; then + level=$highest_level_running + status_prog >& /dev/null + fi + if [ "$missing" != "" ]; then + let level=0-$level + fi + + if [ "$1" != "S" ]; then + echo -n "Currently set level is " + fi + echo $level + if [ "$1" = "S" -o "$1" = "s" ]; then + exit + fi + else + level=-1 + if [ "$1" != "S" ]; then + echo "Currently set level unknown" + fi + fi + # argument -s/-S only returns level, no list + if [ -z "$1" ]; then + status_prog + fi + exit $level } print_level() { - if [ -e /tmp/level.admin ]; then - level=`cat /tmp/level.admin` - if [ "$1" != "P" ]; then - echo -n "Last set level is " - fi - echo $level - if [ "$1" = "P" -o "$1" = "p" ]; then - exit - fi - else - level=-1 - if [ "$1" != "S" ]; then - echo "Last set level unknown" - fi - fi - # argument -s/-S only returns level, no list - exit $level + if [ -e /tmp/level.admin ]; then + level=`cat /tmp/level.admin` + if [ "$1" != "P" ]; then + echo -n "Last set level is " + fi + echo $level + if [ "$1" = "P" -o "$1" = "p" ]; then + exit + fi + else + level=-1 + if [ "$1" != "S" ]; then + echo "Last set level unknown" + fi + fi + # argument -s/-S only returns level, no list + exit $level } show_lofar_version() { - if [ -e $LOFARROOT/Version.txt ]; then - version=`cat $LOFARROOT/Version.txt` - if [ "$1" = "v" ]; then - echo -n "Current LOFAR version is " - fi - echo $version - else - version="-1" - if [ "$1" = "v" ]; then - echo "Current LOFAR version unknown" - fi - fi - if [ "$version" != "-1" ]; then - exit - else - exit $version - fi + if [ -e $LOFARROOT/Version.txt ]; then + version=`cat $LOFARROOT/Version.txt` + if [ "$1" = "v" ]; then + echo -n "Current LOFAR version is " + fi + echo $version + else + version="-1" + if [ "$1" = "v" ]; then + echo "Current LOFAR version unknown" + fi + fi + if [ "$version" != "-1" ]; then + exit + else + exit $version + fi } handle_args() { - # Handle arguments - if [ ${#} -gt 1 ]; then - if [[ $1 != \-* ]]; then - echo "Warning: all arguments except level $1 will be ignored" - fi - fi - while getopts "hUuSsPpVvi:l:q:r:" flag - do - case "$flag" in - [uU]) - show_users=1 - show_level - ;; - [sS]) - show_level $flag - ;; - [pP]) - print_level $flag - ;; - [vV]) - show_lofar_version $flag - ;; - i) - imageForced=1 - image=$OPTARG - # This is needed to be able to retrieve the requested swlevel - # when it is not provided with option -l - shift $((OPTIND-1)); OPTIND=1 - ;; - q) - procesname=$OPTARG - stop_prog $procesname - exit - ;; - r) - procesname=$OPTARG - start_prog $procesname - exit - ;; - l) - level=$OPTARG - ;; - h) - SyntaxError - ;; - *) - exit - ;; - esac - done - if [ -z $level ]; then - if [ "$1" != "" ]; then - level=$1 - else - level=-1 - fi - fi + # Handle arguments + if [ ${#} -gt 1 ]; then + if [[ $1 != \-* ]]; then + echo "Warning: all arguments except level $1 will be ignored" + fi + fi + while getopts "hUuSsPpVvi:l:q:r:" flag + do + case "$flag" in + [uU]) + show_users=1 + show_level + ;; + [sS]) + show_level $flag + ;; + [pP]) + print_level $flag + ;; + [vV]) + show_lofar_version $flag + ;; + i) + imageForced=1 + image=$OPTARG + # This is needed to be able to retrieve the requested swlevel + # when it is not provided with option -l + shift $((OPTIND-1)); OPTIND=1 + ;; + q) + procesname=$OPTARG + stop_prog $procesname + exit + ;; + r) + procesname=$OPTARG + start_prog $procesname + exit + ;; + l) + level=$OPTARG + ;; + h) + SyntaxError + ;; + *) + exit + ;; + esac + done + if [ -z $level ]; then + if [ "$1" != "" ]; then + level=$1 + else + level=-1 + fi + fi - if [ "$user" != "lofarsys" -a $level -gt 3 ]; then - echo "Will only start up to level 3 as this appears to be local use" - level=3 - fi + if [ "$user" != "lofarsys" -a $level -gt 3 ]; then + echo "Will only start up to level 3 as this appears to be local use" + level=3 + fi - return + return } @@ -701,7 +717,7 @@ handle_args() # Find out if we are running on a PVSS system -# Note: on PVSS systems LoggingClient must be ignored, +# Note: on PVSS systems LoggingClient must be ignored, # On non-PVSS system LoggingProcessor. logProgToSkip=LoggingProcessor @@ -712,8 +728,8 @@ fi # All users can ask for current level show_users=0 -if [ -z $1 ]; then - show_level +if [ -z $1 ]; then + show_level fi user=`id | cut -d'(' -f2 | cut -d')' -f1` @@ -724,14 +740,14 @@ handle_args $* # All other options that act on the station status are for lofarsys only # Don't allow root to run swlevel because all logfile get root access. -if [ "$LOFARROOT" == "/opt/lofar" -a "$user" != "lofarsys" -a "$group" != "local" ]; then - echo "swlevel must be run by user lofarsys or group local members!" - exit +if [ "$LOFARROOT" == "/opt/lofar" -a "$user" != "lofarsys" -a "$group" != "local" ]; then + echo "swlevel must be run by user lofarsys or group local members!" + exit fi # first power down to this level case $level in - 0|1|2|3|4|5|6) + 0|1|2|3|4|5|6) ;; *) SyntaxError esac @@ -741,8 +757,8 @@ cd ${BINDIR} goto_level $level cd ${cwd} status_prog -if [ $highest_level_running -gt $level ]; then - echo "Could not go to level $level. Level is $highest_level_running" +if [ $highest_level_running -gt $level ]; then + echo "Could not go to level $level. Level is $highest_level_running" fi # save for later echo $level > /tmp/level.admin diff --git a/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc b/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc index 0fce53909dca9659e70d578925de2f4f84846463..99125ff82515db8570907f1f852b4656d4f5896d 100644 --- a/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc +++ b/MAC/APL/CEPCU/src/PythonControl/PythonControl.cc @@ -212,13 +212,13 @@ bool PythonControl::_startPython(const string& pythonProg, itsPythonName = formatString("PythonServer{%d}@%s", obsID, pythonHost.c_str()); if (onRemoteMachine) { - startCmd = formatString("ssh %s %s %s %s %s %s %s", + startCmd = formatString("ssh %s LOFARENV=$LOFARENV %s %s %s %s %s %s", pythonHost.c_str(), startScript.c_str(), executable.c_str(), parSetName.c_str(), myHostname(true).c_str(), parentService.c_str(), itsPythonName.c_str()); } else { - startCmd = formatString("%s %s %s %s %s %s", + startCmd = formatString("env LOFARENV=$LOFARENV %s %s %s %s %s %s", startScript.c_str(), executable.c_str(), parSetName.c_str(), myHostname(true).c_str(), parentService.c_str(), itsPythonName.c_str()); diff --git a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc index 268a664f815bdd046c72c783ba3a28f8f5019560..ee6cd42d954faf96cb22935cb5b7ff9eaedfb884 100644 --- a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc +++ b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.cc @@ -65,7 +65,7 @@ namespace LOFAR { // static (this) pointer used for signal handling static MACScheduler* pMacScheduler = 0; - + // // MACScheduler() // @@ -108,7 +108,7 @@ MACScheduler::MACScheduler() : } } - ASSERTSTR(itsMaxPlanned + itsMaxFinished < MAX_CONCURRENT_OBSERVATIONS, + ASSERTSTR(itsMaxPlanned + itsMaxFinished < MAX_CONCURRENT_OBSERVATIONS, "maxPlannedList + maxFinishedList should be less than " << MAX_CONCURRENT_OBSERVATIONS); // Read the schedule periods for starting observations. @@ -192,12 +192,12 @@ void MACScheduler::_databaseEventHandler(GCFEvent& event) itsQueuePeriod = newVal; } #endif - } + } break; default: break; - } + } } @@ -211,7 +211,7 @@ GCFEvent::TResult MACScheduler::initial_state(GCFEvent& event, GCFPortInterface& LOG_DEBUG_STR ("initial_state:" << eventName(event)); GCFEvent::TResult status = GCFEvent::HANDLED; - + switch (event.signal) { case F_INIT: break; @@ -235,7 +235,7 @@ GCFEvent::TResult MACScheduler::initial_state(GCFEvent& event, GCFPortInterface& itsTimerPort->setTimer(0.0); } break; - + case F_TIMER: { // must be timer that PropSet is enabled. // update PVSS. LOG_TRACE_FLOW ("Updateing state to PVSS"); @@ -249,7 +249,7 @@ GCFEvent::TResult MACScheduler::initial_state(GCFEvent& event, GCFPortInterface& itsPropertySet->setValue(PN_MS_PLANNED_OBSERVATIONS, GCFPVDynArr(LPT_STRING, emptyArr)); itsPropertySet->setValue(PN_MS_FINISHED_OBSERVATIONS, GCFPVDynArr(LPT_STRING, emptyArr)); - + // Try to connect to the SAS database. ParameterSet* pParamSet = globalParameterSet(); string username = pParamSet->getString("OTDBusername"); @@ -261,7 +261,7 @@ GCFEvent::TResult MACScheduler::initial_state(GCFEvent& event, GCFPortInterface& itsOTDBconnection= new OTDBconnection(username, password, DBname, hostname); ASSERTSTR (itsOTDBconnection, "Memory allocation error (OTDB)"); ASSERTSTR (itsOTDBconnection->connect(), - "Unable to connect to database " << DBname << " on " << hostname << + "Unable to connect to database " << DBname << " on " << hostname << " using " << username << "," << password); LOG_INFO ("Connected to the OTDB"); itsPropertySet->setValue(PN_MS_OTDB_CONNECTED, GCFPVBool(true)); @@ -285,12 +285,12 @@ GCFEvent::TResult MACScheduler::initial_state(GCFEvent& event, GCFPortInterface& case F_DISCONNECTED: break; - + default: LOG_DEBUG ("MACScheduler::initial, default"); status = GCFEvent::NOT_HANDLED; break; - } + } return (status); } @@ -320,15 +320,15 @@ GCFEvent::TResult MACScheduler::recover_state(GCFEvent& event, GCFPortInterface& // TODO: do recovery TRAN(MACScheduler::active_state); - + break; } - + default: LOG_DEBUG("recover_state, default"); status = GCFEvent::NOT_HANDLED; break; - } + } return (status); } @@ -349,7 +349,7 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface& break; case F_ENTRY: { - // install my own signal handler. GCFTask also installs a handler so we have + // install my own signal handler. GCFTask also installs a handler so we have // to install our handler later than the GCFTask handler. pMacScheduler = this; signal (SIGINT, MACScheduler::sigintHandler); // ctrl-c @@ -367,12 +367,12 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface& case F_ACCEPT_REQ: break; - case F_CONNECTED: + case F_CONNECTED: // Should be from the (lost) connection with the SD _connectedHandler(port); break; - case F_DISCONNECTED: + case F_DISCONNECTED: // Can be from StartDaemon or ObsController. // StartDaemon: trouble! Try to reconnect asap. // ObsController: ok when obs is finished, BIG TROUBLE when not! @@ -432,7 +432,7 @@ GCFEvent::TResult MACScheduler::active_state(GCFEvent& event, GCFPortInterface& // -------------------- EVENTS FROM CHILDCONTROL -------------------- // - // That must be events from the ObservationControllers that are currently + // That must be events from the ObservationControllers that are currently // started or running. // case CONTROL_STARTED: { @@ -562,16 +562,16 @@ GCFEvent::TResult MACScheduler::finishing_state(GCFEvent& event, GCFPortInterfac itsTimerPort->setTimer(1L); break; } - + case F_TIMER: GCFScheduler::instance()->stop(); break; - + default: LOG_DEBUG("finishing_state, default"); status = GCFEvent::NOT_HANDLED; break; - } + } return (status); } @@ -632,17 +632,17 @@ void MACScheduler::_updatePlannedList() ASSERTSTR (currentTime != not_a_date_time, "Can't determine systemtime, bailing out"); // get new list (list is ordered on starttime) of planned observations - vector<OTDBtree> plannedDBlist = itsOTDBconnection->getTreeGroup(1, itsPlannedPeriod, itsExclPLcluster); + vector<OTDBtree> plannedDBlist = itsOTDBconnection->getTreeGroup(1, itsPlannedPeriod, itsExclPLcluster); if (!plannedDBlist.empty()) { - LOG_DEBUG(formatString("OTDBCheck:First planned observation (%d) is at %s (active over %d seconds)", - plannedDBlist[0].treeID(), to_simple_string(plannedDBlist[0].starttime).c_str(), + LOG_DEBUG(formatString("OTDBCheck:First planned observation (%d) is at %s (active over %d seconds)", + plannedDBlist[0].treeID(), to_simple_string(plannedDBlist[0].starttime).c_str(), time_duration(plannedDBlist[0].starttime - currentTime).total_seconds())); } // NOTE: do not exit routine on emptylist: we need to write an empty list to clear the DB // make a copy of the current prepared observations (= observations shown in the navigator in the 'future' - // list). By eliminating the observations that are in the current SAS list we end up (at the end of this function) + // list). By eliminating the observations that are in the current SAS list we end up (at the end of this function) // with a list of observations that were in the SASlist the last time but now not anymore. Normally those observations // will appear in the active-list and will be removed there from the prepared list but WHEN AN OPERATOR CHANGES // THE STATUS MANUALLY into something different (e.g. ON-HOLD) the observation stays in the preparedlist. @@ -672,14 +672,14 @@ void MACScheduler::_updatePlannedList() // must we claim this observation at the claimMgr? OLiter prepIter = itsPreparedObs.find(obsID); - if ((prepIter == itsPreparedObs.end()) || (prepIter->second.prepReady == false) || + if ((prepIter == itsPreparedObs.end()) || (prepIter->second.prepReady == false) || (prepIter->second.modTime != modTime)) { // create a ParameterFile for this Observation TreeMaintenance tm(itsOTDBconnection); OTDBnode topNode = tm.getTopNode(obsID); string filename(observationParset(obsID)); if (!tm.exportTree(obsID, topNode.nodeID(), filename)) { - LOG_ERROR_STR ("Cannot create ParameterSet '" << filename << + LOG_ERROR_STR ("Cannot create ParameterSet '" << filename << "' for new observation. Observation CANNOT BE STARTED!"); } else { @@ -694,10 +694,14 @@ void MACScheduler::_updatePlannedList() // otherwise thing will go wrong in the Navigator plannedArr.push_back(new GCFPVString(obsName)); } - + // should this observation (have) be(en) started? int timeBeforeStart = time_duration(plannedDBlist[idx].starttime - currentTime).total_seconds(); -// LOG_DEBUG_STR(obsName << " starts over " << timeBeforeStart << " seconds"); + LOG_INFO(formatString("%s starts at %s which is in %d seconds", + obsName.c_str(), + to_simple_string(plannedDBlist[idx].starttime).c_str(), + timeBeforeStart)); + if (timeBeforeStart > 0 && timeBeforeStart <= (int)itsQueuePeriod) { if (itsPreparedObs[obsID].prepReady == false) { LOG_INFO_STR("Observation " << obsID << " must be started but is not claimed yet."); @@ -710,8 +714,8 @@ void MACScheduler::_updatePlannedList() string cntlrName(controllerName(CNTLRTYPE_OBSERVATIONCTRL, 0, obsID)); if (itsControllerMap.find(cntlrName) == itsControllerMap.end()) { LOG_INFO_STR("Requesting start of " << cntlrName); - itsChildControl->startChild(CNTLRTYPE_OBSERVATIONCTRL, - obsID, + itsChildControl->startChild(CNTLRTYPE_OBSERVATIONCTRL, + obsID, 0, // instanceNr myHostname(true)); // Note: controller is now in state NO_STATE/CONNECTED (C/R) diff --git a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.conf.in b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.conf.in index ed9c486a187ff5e142c65ff6fd5a39ac561f72e8..fd6dd597002a64673a96b3b0da3ea8272152e647 100644 --- a/MAC/APL/MainCU/src/MACScheduler/MACScheduler.conf.in +++ b/MAC/APL/MainCU/src/MACScheduler/MACScheduler.conf.in @@ -33,4 +33,4 @@ ParsetQueuename = lofar.task.specification.system # Pipelines on cluster X can be ignored by the MACScheduler with this key. # use e.g. 'CEP2' or 'CEP4' -excludePipelinesOnThisCluster = '' +excludePipelinesOnThisCluster = 'CEP4' diff --git a/MAC/APL/MainCU/src/ObservationControl/ObservationControl.cc b/MAC/APL/MainCU/src/ObservationControl/ObservationControl.cc index 0e3407d780ee46005368d630fa5dc750017913fa..f8669511d32aa9528a3ae379fcee28feac3d2f98 100644 --- a/MAC/APL/MainCU/src/ObservationControl/ObservationControl.cc +++ b/MAC/APL/MainCU/src/ObservationControl/ObservationControl.cc @@ -253,6 +253,10 @@ void ObservationControl::setState(CTState::CTstateNr newState) message = "Lost connection(s)"; reportState = RTDB_OBJ_STATE_BROKEN; break; + case CT_RESULT_EMERGENCY_TIMEOUT: + message = "Central controller did not die"; + reportState = RTDB_OBJ_STATE_BROKEN; + break; default: message = formatString("Unknown reason(%d)", itsQuitReason); reportState = RTDB_OBJ_STATE_BROKEN; @@ -535,7 +539,17 @@ GCFEvent::TResult ObservationControl::active_state(GCFEvent& event, GCFPortInter } else if (timerEvent.id == itsForcedQuitTimer) { LOG_WARN("QUITING BEFORE ALL CHILDREN DIED."); - itsQuitReason = CT_RESULT_EMERGENCY_TIMEOUT; + + bool centralControllerRunning = + (itsChildControl->countChilds(0, itsProcessType == "Observation" ? CNTLRTYPE_ONLINECTRL : CNTLRTYPE_OFFLINECTRL)); + + if (centralControllerRunning) { + LOG_FATAL("Central controller did not die."); + itsQuitReason = CT_RESULT_EMERGENCY_TIMEOUT; + } else { + // JDM #9487: For stations, a loss of connection is NOT fatal + } + TRAN(ObservationControl::finishing_state); } // some other timer? diff --git a/MAC/APL/PAC/Cal_Server/src/CalServer.cc b/MAC/APL/PAC/Cal_Server/src/CalServer.cc index db8c3ee063b6e7a1acd730f6c8c860a05a8a6a61..76fd4273374757e624cf5f9c715cdc044f9d44b0 100644 --- a/MAC/APL/PAC/Cal_Server/src/CalServer.cc +++ b/MAC/APL/PAC/Cal_Server/src/CalServer.cc @@ -718,22 +718,6 @@ GCFEvent::TResult CalServer::handle_cal_start(GCFEvent& e, GCFPortInterface &por itsSubArrays.scheduleAdd(subarray, &port); // calibration will start within one second - // set the spectral inversion right - RSPSetbypassEvent specInvCmd; - bool SIon(subarray->SPW().nyquistZone() == 2);// on or off? - specInvCmd.timestamp = Timestamp(0,0); - specInvCmd.rcumask = start.rcuMask; - specInvCmd.settings().resize(1); - specInvCmd.settings()(0).setXSI(SIon); - specInvCmd.settings()(0).setYSI(SIon); - LOG_DEBUG_STR("NyquistZone = " << subarray->SPW().nyquistZone() - << " setting spectral inversion " << ((SIon) ? "ON" : "OFF")); - itsRSPDriver->send(specInvCmd); - - //RCUSettings rcu_settings; - //rcu_settings().resize(1); - //rcu_settings()(0).setMode(start.rcumode); - _enableRCUs(subarray, SCHEDULING_DELAY + 4); } } @@ -902,16 +886,26 @@ void CalServer::_enableRCUs(SubArray* subarray, int delay) itsRSPDriver->send(enableCmd); } - // when the lbl inputs are selected swap the X and the Y. - //int rcumode(subarray->SPW().rcumode()); - //int rcumode(0); // TODO: add real rcumode or change all to antennaset - //if (rcumode == 1 || rcumode == 2) { // LBLinput used? + // set the spectral inversion right + bool SIon(subarray->SPW().nyquistZone() == 2);// on or off? + if (SIon) { + RSPSetbypassEvent specInvCmd; + specInvCmd.timestamp = Timestamp(0,0); + specInvCmd.rcumask = rcuMask; + specInvCmd.settings().resize(1); + specInvCmd.settings()(0).setXSI(SIon); + specInvCmd.settings()(0).setYSI(SIon); + LOG_INFO_STR("NyquistZone = " << subarray->SPW().nyquistZone() + << " setting spectral inversion " << ((SIon) ? "ON" : "OFF")); + itsRSPDriver->send(specInvCmd); + } + // when antennaSet = LBA_X or LBA_Y do not swap the X and the Y. if ((subarray->antennaSet() == "LBA_X") || (subarray->antennaSet() == "LBA_Y")) { LOG_INFO("LBL_X or LBA_Y used, swapping not needed"); } + // when the lbl inputs are selected swap the X and the Y. else { - RCUmask_t switchMask; switchMask.reset(); for (uint r = 0; r < m_n_rcus; r++) { @@ -968,6 +962,22 @@ void CalServer::_disableRCUs(SubArray* subarray) // LOG_INFO("No active rcu's anymore, forcing all units to mode 0 and disable"); } + // turn of rcu dataflow if not used anymore + RSPSetrcuEvent disableCmd; + disableCmd.timestamp = Timestamp(0,0); + disableCmd.rcumask = rcus2switchOff; + disableCmd.settings().resize(m_n_rcus); + + for (uint r = 0; r < m_n_rcus; r++) { + if (rcus2switchOff.test(r)) { + disableCmd.settings()(r).setEnable(false); + } + } + LOG_INFO_STR("disableCmd= " << disableCmd); + sleep (1); + LOG_INFO("Disable some rcu's because they are not used anymore"); + itsRSPDriver->send(disableCmd); + // when the lbl inputs are selected swap the X and the Y. LOG_INFO("Resetting swap of X and Y"); RSPSetswapxyEvent swapCmd; @@ -976,6 +986,15 @@ void CalServer::_disableRCUs(SubArray* subarray) swapCmd.antennamask = RCU2AntennaMask(rcus2switchOff); itsRSPDriver->send(swapCmd); + LOG_INFO("Resetting spectral inversion"); + RSPSetbypassEvent specInvCmd; + specInvCmd.timestamp = Timestamp(0,0); + specInvCmd.rcumask = rcus2switchOff; + specInvCmd.settings().resize(1); + specInvCmd.settings()(0).setXSI(false); + specInvCmd.settings()(0).setYSI(false); + itsRSPDriver->send(specInvCmd); + _updateDataStream(0); // asap if (!subarray) { // reset at startup of CalServer or stopping LBA's? diff --git a/MAC/APL/PAC/ITRFBeamServer/src/AnaBeamMgr.cc b/MAC/APL/PAC/ITRFBeamServer/src/AnaBeamMgr.cc index 8eaaf40446c416de6e39646d83a440391d415125..b6e6f01a179ccf0af49e70a6c153644ac4df5a3d 100644 --- a/MAC/APL/PAC/ITRFBeamServer/src/AnaBeamMgr.cc +++ b/MAC/APL/PAC/ITRFBeamServer/src/AnaBeamMgr.cc @@ -69,7 +69,7 @@ AnaBeamMgr::AnaBeamMgr(uint nrRCUsPerRing, // initialize size of array for HBA delay calculation itsHBAdelays.resize(itsSC.nrRSPs * NR_RCUS_PER_RSPBOARD, N_HBA_ELEM_PER_TILE); - + itsHBAdelays(Range::all(), Range::all()) = 0; } // @@ -96,7 +96,7 @@ bool AnaBeamMgr::addBeam(const AnalogueBeam& beam) if (iter != itsBeams.end()) { LOG_ERROR_STR("Beam " << beamName << " already in my admistration"); return (false); - } + } // remember the beam itsBeams[beamName] = beam; @@ -120,13 +120,13 @@ bool AnaBeamMgr::addBeam(const AnalogueBeam& beam) // // deleteBeam(beam) // -void AnaBeamMgr::deleteBeam(const string& beamName) +void AnaBeamMgr::deleteBeam(const string& beamName) { map<string, AnalogueBeam>::const_iterator iter = itsBeams.find(beamName); if (iter == itsBeams.end()) { LOG_ERROR_STR("Beam " << beamName << " is not in my administration, it cannot be deleted"); return; - } + } itsBeams.erase(beamName); // delete all pointings @@ -153,7 +153,7 @@ bool AnaBeamMgr::addPointing(const string& beamName, const Pointing& newPt) if (iter == itsBeams.end()) { LOG_ERROR_STR("Beam " << beamName << " is not in my administration, pointing rejected"); return (false); - } + } // add the pointing of this beam PointingInfo PI; @@ -197,7 +197,7 @@ void AnaBeamMgr::activateBeams(const Timestamp& now) iter->active = false; } bool beamIsActive(iter->active); // remember state of this beam - + // delete old (expired) pointings of this beam while (iter != end && iter->beam.name() == beamName && iter->pointing.endTime() < now) { LOG_DEBUG_STR("Removing pointing " << beamName << ":" << iter->pointing); @@ -209,12 +209,12 @@ void AnaBeamMgr::activateBeams(const Timestamp& now) LOG_INFO_STR("Beam " << beamName << " has ended"); continue; } - + if (beamIsActive) { // active beams should stay active. iter->active = true; // make that pointing the active one itsActiveRCUs |= iter->beam.rcuMask(); // update occupied rcus } - else { // beam is not active try to activate it + else { // beam is not active try to activate it // activate the beam when that is possible. if (conflictingRCUs.none() && iter->pointing.time() <= now) { iter->active = true; @@ -222,7 +222,7 @@ void AnaBeamMgr::activateBeams(const Timestamp& now) LOG_INFO_STR("Beam " << beamName << " is switched ON"); } } - + // skip rest of the pointings of this beam while (iter != end && iter->beam.name() == beamName) { ++iter; @@ -275,7 +275,7 @@ void AnaBeamMgr::calculateHBAdelays(RTC::Timestamp targetTime, CasaConverter& aJ showAdmin(); LOG_INFO_STR("Calculating HBAdelays for time " << targetTime); - itsHBAdelays(Range::all(), Range::all()) = 0; + //itsHBAdelays(Range::all(), Range::all()) = 0; itsTargetTime = targetTime; // When have to do the calculations on the HBA, HBA0 and HBA1 field. (at least we have to check those fields). @@ -300,7 +300,7 @@ void AnaBeamMgr::calculateHBAdelays(RTC::Timestamp targetTime, CasaConverter& aJ continue; // try next field } - // we found an active beam in this HBA field, calculate the delays for all + // we found an active beam in this HBA field, calculate the delays for all // active beams in this field // Get geographical location of antennaField in ITRF @@ -319,7 +319,7 @@ void AnaBeamMgr::calculateHBAdelays(RTC::Timestamp targetTime, CasaConverter& aJ (itsTileRelPos(i,2) * itsTileRelPos(i,2))); } LOG_DEBUG_STR("tileRelLength:" << tileRelLength); - + // for all active pointings in this antennafield while (pIter != pEnd) { // must be of the same antenna field. @@ -369,45 +369,53 @@ void AnaBeamMgr::calculateHBAdelays(RTC::Timestamp targetTime, CasaConverter& aJ } // Do the HBA_1 antennas use a different rotation and are we calculating such an antenna? - int HBAdeltaOffset = (itsDiffHBArotations && (rcu/N_POL >= (itsSC.nrHBAs/2)) + int HBAdeltaOffset = (itsDiffHBArotations && (rcu/N_POL >= (itsSC.nrHBAs/2)) ? N_HBA_ELEM_PER_TILE : 0); for (int element = 0; element < N_HBA_ELEM_PER_TILE; ++element) { // calculate tile delay. double delay = ( (sourceJ2000xyz(0,0) * tileRelPosJ2000(HBAdeltaOffset + element,0)) + (sourceJ2000xyz(0,1) * tileRelPosJ2000(HBAdeltaOffset + element,1)) + - (sourceJ2000xyz(0,2) * tileRelPosJ2000(HBAdeltaOffset + element,2)) + (sourceJ2000xyz(0,2) * tileRelPosJ2000(HBAdeltaOffset + element,2)) ) / SPEED_OF_LIGHT_MS; - + // signal front stays in the middle of the tile delay += itsMeanElementDelay; -// LOG_DEBUG_STR("antenna="<<rcu/2 <<", pol="<<rcu%2 <<", element="<<element <<", delay("<<rcu<<","<<element<<")="<<delay); - // calculate approximate DelayStepNr - int delayStepNr = static_cast<int>(delay / 0.5E-9); - // range check for delayStepNr, max. 32 steps (0..31) - delayStepNr = MIN2(delayStepNr, maxStepNr); // limit against upper boundary + int delayStepNr = static_cast<int>((itsHBAdelays(rcu, element) & 0x7c) >> 2); // set actual delay + + // search for next step + for (int i = 0; i < maxStepNr; ++i) { + if (fabs(delay - itsDelaySteps(i)) <= 0.2E-9) { + delayStepNr = i; + break; + } + } + + if (fabs(delay - itsDelaySteps(delayStepNr)) > 0.6E-9) { + LOG_INFO_STR("delay diff > 0.75E-9, calculate approximate delaynr"); + delayStepNr = static_cast<int>(fabs(delay) / 0.5E-9); + } + + delayStepNr = MIN2(delayStepNr, maxStepNr); // limit against upper boundary delayStepNr = MAX2(0, delayStepNr); // limit against lower boundary - - // look for nearest matching delay step in range "delayStepNr - 2 .. delayStepNr + 2" - double minDelayDiff = fabs(delay - itsDelaySteps(delayStepNr)); - double difference; - int minStepNr = delayStepNr; - int stepMinusTwo = MAX2(0, delayStepNr-2); // limit check to element 0 - int stepPlusTwo = MIN2(maxStepNr, delayStepNr+2); // limit check to element 31 - for (int i = stepMinusTwo; i <= stepPlusTwo; i++){ - if (i == delayStepNr) - continue; // skip approximate nr - difference = fabs(delay - itsDelaySteps(i)); - if (difference < minDelayDiff) { - minStepNr = i; - minDelayDiff = difference; - } - } - delayStepNr = minStepNr; - - // bit1=0.25nS(not used), bit2=0.5nS, bit3=1nS, bit4=2nS, bit5=4nS, bit6=8nS - itsHBAdelays(rcu,element) = (delayStepNr * 4) + (1 << 7); // assign + + if (itsDiffHBArotations) { + if ((rcu == 0) && (rcu%2 == 0)) { + LOG_INFO_STR("HBA-0 delay, element="<<element <<", requested delay="<<delay <<", real delay="<<itsDelaySteps(delayStepNr)); + } + if ((rcu == 48) && (rcu%2 == 0)) { + LOG_INFO_STR("HBA-1 delay, element="<<element <<", requested delay="<<delay <<", real delay="<<itsDelaySteps(delayStepNr)); + } + } + else { + if ((rcu == 0) && (rcu%2 == 0)) { + LOG_INFO_STR("HBA delay, element="<<element <<", requested delay="<<delay <<", real delay="<<itsDelaySteps(delayStepNr)); + } + } + + // bit1=0.25nS(not used), bit2=0.5nS, bit3=1nS, bit4=2nS, bit5=4nS, bit6=8nS + itsHBAdelays(rcu, element) = (delayStepNr << 2) + (1 << 7); // assign } // for element } // rcus ++pIter; @@ -423,7 +431,7 @@ void AnaBeamMgr::sendHBAdelays(GCF::TM::GCFPortInterface& port) // note: the RSPDriver expects settings for the active RCU's only. // TODO: CHANGE THIS SO WE CAN POINT THE UNUSED TILES TO ZENITH request.settings().resize(itsActiveRCUs.count(), N_HBA_ELEM_PER_TILE); - int nrRCUs = itsActiveRCUs.size(); + int nrRCUs = itsActiveRCUs.size(); int requestIdx(0); for (int r = 0; r < nrRCUs; r++) { if (itsActiveRCUs.test(r)) { @@ -449,7 +457,7 @@ void AnaBeamMgr::sendHBAdelays(GCF::TM::GCFPortInterface& port) // --------------- READ CONFIGURATIONFILES --------------- // -// getAllHBADeltas +// getAllHBADeltas // void AnaBeamMgr::getAllHBADeltas(const string& filename) { @@ -472,14 +480,14 @@ void AnaBeamMgr::getAllHBADeltas(const string& filename) LOG_DEBUG_STR("HBADeltas comment = " << itsName); getline(itsFile, itsName); // read name or comment } - + if ("" == itsName) { itsFile.close(); return; } - + LOG_DEBUG_STR("HBADeltas name = " << itsName); - + // Now comment lines are skipped, so we can read the full array. itsFile >> itsTileRelPos; // read HBA deltas array LOG_DEBUG_STR("HBADeltas = " << itsTileRelPos); @@ -492,7 +500,7 @@ void AnaBeamMgr::getAllHBADeltas(const string& filename) } // -// getAllHBAElementDelays +// getAllHBAElementDelays // void AnaBeamMgr::getAllHBAElementDelays(const string& filename) { @@ -502,7 +510,7 @@ void AnaBeamMgr::getAllHBAElementDelays(const string& filename) LOG_DEBUG_STR("Trying to read the HBA element delay steps from file " << filename); // open new file - if (itsFile.is_open()) { + if (itsFile.is_open()) { itsFile.close(); } itsFile.open(filename.c_str()); @@ -516,14 +524,14 @@ void AnaBeamMgr::getAllHBAElementDelays(const string& filename) LOG_DEBUG_STR("HBA ElementDelays comment = " << itsName); getline(itsFile, itsName); // read name or comment } - + if ("" == itsName) { itsFile.close(); return; } LOG_DEBUG_STR("HBA ElementDelays Name = " << itsName); - + // Now comment lines are skipped, so we can read the full array. itsFile >> itsDelaySteps; // read HBA element delays array //itsDelaySteps *= 1E-9; // convert from nSec to Secs diff --git a/MAC/APL/PAC/ITRFBeamServer/src/BeamServer.cc b/MAC/APL/PAC/ITRFBeamServer/src/BeamServer.cc index 1c1d08bf26f7a940ee8cbbab212bf096a9a92457..7d0143d7c6fa5099f274226ea7e736d9e3bf33b7 100644 --- a/MAC/APL/PAC/ITRFBeamServer/src/BeamServer.cc +++ b/MAC/APL/PAC/ITRFBeamServer/src/BeamServer.cc @@ -827,7 +827,7 @@ GCFEvent::TResult BeamServer::beamalloc_state(GCFEvent& event, GCFPortInterface& IBSBeamallocackEvent beamallocack; if (ack.status == CAL_Protocol::CAL_SUCCESS) { // && -//TODO (ack.subarray.SPW().getNumSubbands() >= +//TODO (GET_CONFIG("CalServer.N_SUBBANDS", i) >= //TODO (int)itsBeamTransaction.getBeam()->allocation()().size()) ) { LOG_INFO_STR("Got subscription to subarray " << ack.subarray.name()); @@ -879,7 +879,7 @@ GCFEvent::TResult BeamServer::beamalloc_state(GCFEvent& event, GCFPortInterface& // Timer event may be from one of the pointing timers, ignore them LOG_INFO_STR(">>> TimerEvent on port " << port.getName() << " while in alloc state, ignoring it"); return (GCFEvent::HANDLED); -// return ((&port==itsAnaHeartbeat) ? GCFEvent::NEXT_STATE : GCFEvent::HANDLED); TODO: FIX THIS +// return ((&port==itsAnaHeartbeat) ? GCFEvent::NEXT_STATE : GCFEvent::HANDLED); // TODO: FIX THIS } IBSBeamallocackEvent beamallocack; LOG_ERROR_STR("Timeout on starting the calibration of beam " << itsBeamTransaction.getBeam()->name()); @@ -1109,14 +1109,19 @@ int BeamServer::beampointto_action(IBSPointtoEvent& ptEvent, // The analogue must be started several seconds before the observationstart time because the I2C bus // needs several seconds to pass the information - int activationTime = _idealStartTime(Timestamp::now().sec(), - ptEvent.pointing.time(), HBA_MIN_INTERVAL, - itsLastHBACalculationTime+itsHBAUpdateInterval, HBA_MIN_INTERVAL, itsHBAUpdateInterval); - int timeShift(ptEvent.pointing.time().sec() - activationTime); - // update pointing + int activationTime = _idealStartTime( Timestamp::now().sec(), + ptEvent.pointing.time(), + HBA_MIN_INTERVAL, + itsLastHBACalculationTime + itsHBAUpdateInterval, + HBA_MIN_INTERVAL, + itsHBAUpdateInterval); + + int timeShift(ptEvent.pointing.time().sec() - activationTime); + + // update pointing ptEvent.pointing.setTime(Timestamp(activationTime,0)); if (ptEvent.pointing.duration() && timeShift) { - ptEvent.pointing.setDuration(ptEvent.pointing.duration()+timeShift); + ptEvent.pointing.setDuration(ptEvent.pointing.duration()); LOG_INFO_STR("Extended duration of " << beamIter->second->name() << " with " << timeShift << " seconds because starttime was shifted"); } @@ -1127,7 +1132,9 @@ int BeamServer::beampointto_action(IBSPointtoEvent& ptEvent, return (IBS_UNKNOWN_BEAM_ERR); } - itsAnaHeartbeat->setTimer(activationTime - Timestamp::now().sec()); + itsAnaHeartbeat->cancelAllTimers(); + itsAnaHeartbeat->setTimer(activationTime - Timestamp::now().sec(), 0, itsHBAUpdateInterval, 0); // restart analog beam hartbeat + // itsAnaHeartbeat->setTimer(activationTime - Timestamp::now().sec()); LOG_INFO_STR("Analogue beam for beam " << beamIter->second->name() << " will be active at " << Timestamp(activationTime+HBA_MIN_INTERVAL, 0)); LOG_INFO_STR("Analogue pointing for beam " << beamIter->second->name() << " will be send at " << Timestamp(activationTime, 0)); @@ -1152,12 +1159,16 @@ int BeamServer::beampointto_action(IBSPointtoEvent& ptEvent, // int BeamServer::_idealStartTime (int now, int t1, int d1, int t2, int d2, int p2) const { - int t1start = t1-d1; // ideal starttime + int t1start = t1 - d1; // ideal starttime if (t1start < now) // not before now ofcourse t1start = now; - int nearestt2 = (t1start<=t2 ? t2 : t2+((t1-t2)/p2)*p2); - if (t1start > nearestt2 && t1start < nearestt2+d2) // not during heartbeat period - t1start = nearestt2; + + if (t1start <= (t2 - p2 + d2)) // if it is to fast after previous update + t1start = t2 - p2 + d2 + 1; + + //int nearestt2 = (t1start <= t2 ? t2 : t2 + ((t1 - t2) / p2) * p2); + //if (t1start > nearestt2 && t1start < nearestt2 + d2) // not during heartbeat period + // t1start = nearestt2; return (t1start); } diff --git a/MAC/APL/PAC/ITRFBeamServer/src/beamctl.cc b/MAC/APL/PAC/ITRFBeamServer/src/beamctl.cc index c53e2665b15be5d0f9bdd3e00a95255b8f5b2493..f33330f7c6454035a28508113ce3e7a55d34db0e 100644 --- a/MAC/APL/PAC/ITRFBeamServer/src/beamctl.cc +++ b/MAC/APL/PAC/ITRFBeamServer/src/beamctl.cc @@ -242,6 +242,7 @@ GCFEvent::TResult beamctl::create_subarray(GCFEvent& event, GCFPortInterface& po TRAN(beamctl::final); } else { cout << "Calserver accepted settings." << endl; + sleep(3 + 4); TRAN(beamctl::create_beam); } } @@ -378,6 +379,9 @@ GCFEvent::TResult beamctl::sendPointings(GCFEvent& event, GCFPortInterface& port IBSPointtoackEvent ack(event); if (ack.status != IBS_Protocol::IBS_NO_ERR) { cerr << "Error: " << errorName(ack.status) << endl; + if (ack.status == IBS_Protocol::IBS_UNKNOWN_BEAM_ERR) { + cerr << " Possible wrong bitmode ?" << endl; + } TRAN(beamctl::final); } else { TRAN(beamctl::sendPointings); // tran to myself to exec the ENTRY state again. diff --git a/MAC/APL/PAC/ITRFBeamServer/src/iBeamServer.conf.in b/MAC/APL/PAC/ITRFBeamServer/src/iBeamServer.conf.in index 751bdde98731643383009d010f4bc9d7946ee340..e96a31d9af92978b42f116b51d00c5176296057c 100644 --- a/MAC/APL/PAC/ITRFBeamServer/src/iBeamServer.conf.in +++ b/MAC/APL/PAC/ITRFBeamServer/src/iBeamServer.conf.in @@ -11,10 +11,10 @@ BeamServer.EnableSetHBA = 1 BeamServer.EnableStaticCalibration = 1 # -# normal HBAUpdateInterval >= 10, for testing use +# normal HBAUpdateInterval >= 10, for testing use # (4, 6 or 8) secs. Note: 4 sec is absolute minimum! # -BeamServer.HBAUpdateInterval= 180 # >=10, normally 300 +BeamServer.HBAUpdateInterval= 10 # >=10, normally 10 BeamServer.UpdateInterval = 1 # should be 1 for normal operation BeamServer.ComputeInterval = 1 # can be 1 for ITRFBeamserver, was 10 for old BeamServer diff --git a/MAC/APL/PIC/RSP_Driver/src/CRSyncWrite.cc b/MAC/APL/PIC/RSP_Driver/src/CRSyncWrite.cc index 59e298124009a59010b9f1c6a96398b766b1b3ae..7974531e20e6d9e8e40be7a1a5ef2125490562db 100644 --- a/MAC/APL/PIC/RSP_Driver/src/CRSyncWrite.cc +++ b/MAC/APL/PIC/RSP_Driver/src/CRSyncWrite.cc @@ -42,13 +42,15 @@ using namespace RTC; CRSyncWrite::CRSyncWrite(GCFPortInterface& board_port, int board_id) : SyncAction(board_port, board_id, NR_BLPS_PER_RSPBOARD) { - - //ASSERTSTR(delaySteps.extent(firstDim) == NR_BLPS_PER_RSPBOARD, "Expected " << NR_BLPS_PER_RSPBOARD << + + //ASSERTSTR(delaySteps.extent(firstDim) == NR_BLPS_PER_RSPBOARD, "Expected " << NR_BLPS_PER_RSPBOARD << // " delay-steps not " << delaySteps.extent(firstDim)); itsDelays.resize(NR_BLPS_PER_RSPBOARD); itsCounters.resize(NR_BLPS_PER_RSPBOARD); - + itsDelays = 0; + itsCounters = 0; + //itsDelays.resize(delaySteps.shape()); //itsCounters.resize(delaySteps.shape()); //itsDelays = delaySteps; @@ -77,17 +79,17 @@ void CRSyncWrite::sendrequest() uint16 blpid(0); EPACrControlEvent CRSyncEvent; if (Cache::getInstance().getState().crcontrol().get(getBoardId()) == RTC::RegisterState::READ) { - + int sliceBegin = getBoardId() * NR_BLPS_PER_RSPBOARD; int sliceEnd = sliceBegin + NR_BLPS_PER_RSPBOARD - 1; itsDelays = Cache::getInstance().getBack().getPPSdelays()(Range(sliceBegin, sliceEnd)); - + setNumIndices(max(itsDelays)+1); - + // start of cycle, log unfinished cycles for (int b = 0; b < NR_BLPS_PER_RSPBOARD; b++) { if (itsCounters(b) && itsCounters(b) != itsDelays(b)) { - LOG_WARN(formatString("PPS delay[%d][%]: %d out of %d written", + LOG_WARN(formatString("PPS delay[%d][%d]: %d out of %d written", getBoardId(), b, itsDelays(b)-itsCounters(b), itsDelays(b))); } } @@ -104,7 +106,7 @@ void CRSyncWrite::sendrequest() blpid |= mask; itsCounters(b)--; } - } + } CRSyncEvent.hdr.set(MEPHeader::CR_SYNCDELAY_HDR, blpid); CRSyncEvent.control = 0x01; // SyncEdge=rise, SyncDelay=Increment } diff --git a/MAC/APL/PIC/RSP_Driver/src/Cache.cc b/MAC/APL/PIC/RSP_Driver/src/Cache.cc index e8c130e2cc8d629352e7e62308876ec310d4debe..9cfa3510045adbae7bc9151e0a3589c6324643f1 100644 --- a/MAC/APL/PIC/RSP_Driver/src/Cache.cc +++ b/MAC/APL/PIC/RSP_Driver/src/Cache.cc @@ -141,7 +141,7 @@ CacheBuffer::CacheBuffer(Cache* cache) : m_cache(cache) m_clock = GET_CONFIG("RSPDriver.DEFAULT_SAMPLING_FREQUENCY", i); reset(); // reset by allocating memory and settings default values - + // print sizes of the cache LOG_DEBUG_STR("m_beamletweights().size() =" << m_beamletweights().size() * sizeof(complex<int16>)); LOG_DEBUG_STR("m_subbandselection.crosslets().size()=" << m_subbandselection.crosslets().size() * sizeof(uint16)); @@ -176,22 +176,22 @@ CacheBuffer::CacheBuffer(Cache* cache) : m_cache(cache) LOG_DEBUG_STR("itsFixedAttenuations.size() =" << sizeof(itsFixedAttenuations)); LOG_DEBUG_STR("itsAttenuationStepSize.size() =" << sizeof(itsAttenuationStepSize)); LOG_INFO_STR(formatString("CacheBuffer size = %d bytes", - m_beamletweights().size() - + m_subbandselection.crosslets().size() - + m_subbandselection.beamlets().size() - + m_rcusettings().size() - + m_hbasettings().size() - + m_hbareadings().size() - + m_rsusettings().size() - + m_wgsettings().size() - + m_subbandstats().size() - + m_beamletstats().size() - + m_xcstats().size() + m_beamletweights().size() + + m_subbandselection.crosslets().size() + + m_subbandselection.beamlets().size() + + m_rcusettings().size() + + m_hbasettings().size() + + m_hbareadings().size() + + m_rsusettings().size() + + m_wgsettings().size() + + m_subbandstats().size() + + m_beamletstats().size() + + m_xcstats().size() + m_systemstatus.board().size() - + m_versions.bp().size() - + m_versions.ap().size() - + m_tdstatus.board().size() - + m_spustatus.subrack().size() + + m_versions.bp().size() + + m_versions.ap().size() + + m_tdstatus.board().size() + + m_spustatus.subrack().size() + m_tbbsettings().size() + m_bypasssettings().size() + m_bypasssettings_bp().size() @@ -249,10 +249,10 @@ void CacheBuffer::reset(void) itsBitsPerSample = MAX_BITS_PER_SAMPLE; itsSDOBitsPerSample = MAX_BITS_PER_SAMPLE; - - m_beamletweights().resize( BeamletWeights::SINGLE_TIMESTEP, + + m_beamletweights().resize( BeamletWeights::SINGLE_TIMESTEP, StationSettings::instance()->nrRcus(), - MAX_NR_BM_BANKS, + MAX_NR_BM_BANKS, MEPHeader::N_BEAMLETS); m_beamletweights() = complex<int16>(25,36); // TODO remove this code!!! @@ -274,7 +274,7 @@ void CacheBuffer::reset(void) (MAX_BITS_PER_SAMPLE/MIN_BITS_PER_SAMPLE), MEPHeader::N_BEAMLETS ); m_subbandselection.beamlets() = 0; - + if (GET_CONFIG("RSPDriver.IDENTITY_WEIGHTS", i)) { // these weights ensure that the beamlet statistics // exactly match the subband statistics @@ -285,7 +285,7 @@ void CacheBuffer::reset(void) // int firstSubband = GET_CONFIG("RSPDriver.FIRST_SUBBAND", i); for (int rcu = 0; rcu < m_subbandselection.beamlets().extent(firstDim); rcu++) { - for (int bank = 0; bank < (MAX_BITS_PER_SAMPLE/MIN_BITS_PER_SAMPLE); bank++) { + for (int bank = 0; bank < (MAX_BITS_PER_SAMPLE/MIN_BITS_PER_SAMPLE); bank++) { for (int lane = 0; lane < MEPHeader::N_SERDES_LANES; lane++) { int start(lane*(MEPHeader::N_BEAMLETS/MEPHeader::N_SERDES_LANES)); int stop (start + maxBeamletsPerRSP(itsBitsPerSample)); @@ -333,9 +333,9 @@ void CacheBuffer::reset(void) m_subbandstats().resize(StationSettings::instance()->nrRcus(), MEPHeader::N_SUBBANDS); m_subbandstats() = 0; - + // Number of cep streams -> in normal mode 4, in splitmode 8. - int maxStreams = 8; + int maxStreams = 8; m_beamletstats().resize((maxStreams/MEPHeader::N_SERDES_LANES) * N_POL, (MAX_BITS_PER_SAMPLE/MIN_BITS_PER_SAMPLE) * MEPHeader::N_BEAMLETS); m_beamletstats() = 0; @@ -382,14 +382,14 @@ void CacheBuffer::reset(void) m_bypasssettings().resize(StationSettings::instance()->nrBlps()); BypassSettings::Control control; m_bypasssettings() = control; - + // BypassSettings (BP) LOG_INFO_STR("Resizing bypass array to: " << StationSettings::instance()->maxRspBoards()); m_bypasssettings_bp().resize(StationSettings::instance()->maxRspBoards()); BypassSettings::Control controlbp; controlbp.setSDO(1); m_bypasssettings_bp() = controlbp; - + // clear rawdatablock itsRawDataBlock.address = 0; itsRawDataBlock.offset = 0; @@ -404,40 +404,40 @@ void CacheBuffer::reset(void) // clear I2C flag itsI2Cuser = NONE; - + // set Splitter not active itsSplitterActive = false; // set CEP port enabled itsCepEnabled0 = false; itsCepEnabled1 = false; - + // Latency status itsLatencys().resize(StationSettings::instance()->nrRspBoards()); RADLatency radlatencyinit; memset(&radlatencyinit, 0, sizeof(RADLatency)); itsLatencys() = radlatencyinit; itsSwappedXY.reset(); - + // BitMode itsBitModeInfo().resize(StationSettings::instance()->nrRspBoards()); RSRBeamMode bitmodeinfo; bitmodeinfo.bm_select = 0; bitmodeinfo.bm_max = 0; itsBitModeInfo() = bitmodeinfo; - + // SDO default Mode selection int sdo_mode = 0; int bits_per_sample = GET_CONFIG("RSPDriver.SDO_MODE", i); if (bits_per_sample == 8) { sdo_mode = 1; } else if (bits_per_sample == 5) { sdo_mode = 2; } else if (bits_per_sample == 4) { sdo_mode = 3; } - + itsSDOModeInfo().resize(StationSettings::instance()->nrRspBoards()); RSRSDOMode sdomodeinfo; sdomodeinfo.bm_select = sdo_mode; sdomodeinfo.bm_max = 3; itsSDOModeInfo() = sdomodeinfo; - + // SDO default subband selection itsSDOSelection.subbands().resize(StationSettings::instance()->nrRcus(), (MAX_BITS_PER_SAMPLE/MIN_BITS_PER_SAMPLE), @@ -455,7 +455,7 @@ void CacheBuffer::reset(void) } // for each bank } readPPSdelaySettings(); - + itsAttenuationStepSize = 0.25; try { itsAttenuationStepSize = GET_CONFIG("RSPDriver.ATT_STEP_SIZE", f); } catch (APSException&) { LOG_INFO_STR("RSPDriver.ATT_STEP_SIZE not found"); } @@ -467,13 +467,13 @@ void CacheBuffer::reset(void) itsFixedAttenuations(rcumode) = 0.0; try { itsFixedAttenuations(rcumode) = GET_CONFIG(key, f); } catch (APSException&) { LOG_INFO_STR(formatString("RSPDriver.FIXED_ATT_MODE_%d not found", rcumode)); } - } + } } SerdesBuffer& CacheBuffer::getSdsReadBuffer(int rspBoardNr) { - ASSERTSTR(rspBoardNr >= 0 && rspBoardNr < MAX_N_RSPBOARDS, + ASSERTSTR(rspBoardNr >= 0 && rspBoardNr < MAX_N_RSPBOARDS, "RSPboard index out of range in getting serdesReadBuffer: " << rspBoardNr); return (itsSdsReadBuffer[rspBoardNr]); } @@ -488,7 +488,11 @@ void CacheBuffer::setTimestamp(const RTC::Timestamp& timestamp) // void CacheBuffer::readPPSdelaySettings() { - ConfigLocator CL; + if (m_clock == 0) { + LOG_INFO_STR("Skip loading PPS delay settings, clock = 0 MHz"); + return; + } + ConfigLocator CL; string filename = (CL.locate(GET_CONFIG_STRING(formatString("RSPDriver.PPSdelayFile%u", m_clock)))); LOG_DEBUG_STR("Trying to load the PPS delay settings for " << m_clock << " MHz from file: " << filename); @@ -523,7 +527,7 @@ void CacheBuffer::readPPSdelaySettings() } else { LOG_ERROR_STR("File " << filename << " contains " << delayValues.extent(firstDim) - << " values, expected " << nrRspBoards * NR_BLPS_PER_RSPBOARD + << " values, expected " << nrRspBoards * NR_BLPS_PER_RSPBOARD << " values, WILL NOT USE THEM!"); } std::ostringstream logStream; @@ -598,8 +602,8 @@ void Cache::resetI2Cuser() } m_front->setI2Cuser(busUser); m_back->setI2Cuser (busUser); - LOG_INFO_STR("new I2Cuser = " << ((busUser == NONE) ? "NONE" : - ((busUser == HBA) ? "HBA" : + LOG_INFO_STR("new I2Cuser = " << ((busUser == NONE) ? "NONE" : + ((busUser == HBA) ? "HBA" : ((busUser == RCU_R) ? "RCU_R" : "RCU_W")))); } diff --git a/MAC/APL/PIC/RSP_Driver/src/GetRCUCmd.cc b/MAC/APL/PIC/RSP_Driver/src/GetRCUCmd.cc index 5e49ffc29be910ceea380d8c18fbedc774d3013d..8a20f977cadef3f570e2d58674fc9c92909511ac 100644 --- a/MAC/APL/PIC/RSP_Driver/src/GetRCUCmd.cc +++ b/MAC/APL/PIC/RSP_Driver/src/GetRCUCmd.cc @@ -66,7 +66,7 @@ void GetRCUCmd::ack(CacheBuffer& cache) ack.settings()(result_rcu).setRaw(cache.getRCUSettings()()(cache_rcu).getRaw()); } else { - LOG_WARN(formatString("invalid RCU index %d, there are only %d RCU's", cache_rcu, + LOG_WARN(formatString("invalid RCU index %d, there are only %d RCU's", cache_rcu, StationSettings::instance()->nrRcus())); } @@ -82,8 +82,8 @@ void GetRCUCmd::apply(CacheBuffer& cache, bool setModFlag) { // someone else using the I2C bus? I2Cuser busUser = cache.getI2Cuser(); - LOG_INFO_STR("GetRCU::apply : " << ((busUser == NONE) ? "NONE" : - ((busUser == HBA) ? "HBA" : + LOG_DEBUG_STR("GetRCU::apply : " << ((busUser == NONE) ? "NONE" : + ((busUser == HBA) ? "HBA" : ((busUser == RCU_R) ? "RCU_R" : "RCU_W")))); if ((cache.getI2Cuser() != NONE) && (cache.getI2Cuser() != RCU_R)) { postponeExecution(true); @@ -96,7 +96,7 @@ void GetRCUCmd::apply(CacheBuffer& cache, bool setModFlag) for (int cache_rcu = 0; cache_rcu < StationSettings::instance()->nrRcus(); cache_rcu++) { if (setModFlag && m_event->rcumask.test(cache_rcu)) { cache.getCache().getState().rcuread().write(cache_rcu); - } + } } // for } diff --git a/MAC/APL/PIC/RSP_Driver/src/GetSplitterCmd.cc b/MAC/APL/PIC/RSP_Driver/src/GetSplitterCmd.cc index a38993cfdaf6fc7415e7067657a49c58392d220a..153850441602cfa203b6673a458c767315b82b66 100644 --- a/MAC/APL/PIC/RSP_Driver/src/GetSplitterCmd.cc +++ b/MAC/APL/PIC/RSP_Driver/src/GetSplitterCmd.cc @@ -127,10 +127,11 @@ void GetSplitterCmd::apply(CacheBuffer& cache, bool setModFlag) // the sdsReadBuffer if for storing the results. SerdesBuffer& SerdesBuf = cache.getSdsWriteBuffer(); SerdesBuf.newCommand(ASK_SERDES_CMD, ASK_SERDES_CMD_LEN); +#if 0 string hd; hexdump(hd, SerdesBuf.getBufferPtr(), SerdesBuf.getDataLen()); LOG_INFO_STR(hd); - +#endif // mark registers that the serdes registers should be written. if (setModFlag) { for (int b= 0; b < StationSettings::instance()->nrRspBoards(); b++) { diff --git a/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.cc b/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.cc index 63a2e2520577e350a77823cf4227b0ae466bb392..5b8ccb4bfa802f56ba0e47b7fd7ffba9b7f3f875 100644 --- a/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.cc +++ b/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.cc @@ -53,41 +53,41 @@ namespace LOFAR { // the indices that should be patched. // int HBAProtocolWrite::i2c_protocol_patch_indices[] = { - 8, 9, - 51 + ( 0*17), // stride is 17, server 1 - 51 + ( 1*17), - 51 + ( 2*17), - 51 + ( 3*17), - 51 + ( 4*17), - 51 + ( 5*17), - 51 + ( 6*17), - 51 + ( 7*17), - 51 + ( 8*17), - 51 + ( 9*17), - 51 + (10*17), - 51 + (11*17), - 51 + (12*17), - 51 + (13*17), - 51 + (14*17), - 51 + (15*17), // server 16 + 13, 14, + 56 + ( 0*17), // stride is 17, server 1 + 56 + ( 1*17), + 56 + ( 2*17), + 56 + ( 3*17), + 56 + ( 4*17), + 56 + ( 5*17), + 56 + ( 6*17), + 56 + ( 7*17), + 56 + ( 8*17), + 56 + ( 9*17), + 56 + (10*17), + 56 + (11*17), + 56 + (12*17), + 56 + (13*17), + 56 + (14*17), + 56 + (15*17), // server 16 }; int HBAProtocolWrite::i2c_result_patch_indices[] = { - 4 + ( 0*7), // stride is 7, server 1 - 4 + ( 1*7), - 4 + ( 2*7), - 4 + ( 3*7), - 4 + ( 4*7), - 4 + ( 5*7), - 4 + ( 6*7), - 4 + ( 7*7), - 4 + ( 8*7), - 4 + ( 9*7), - 4 + (10*7), - 4 + (11*7), - 4 + (12*7), - 4 + (13*7), - 4 + (14*7), - 4 + (15*7), // server 16 + 5 + ( 0*7), // stride is 7, server 1 + 5 + ( 1*7), + 5 + ( 2*7), + 5 + ( 3*7), + 5 + ( 4*7), + 5 + ( 5*7), + 5 + ( 6*7), + 5 + ( 7*7), + 5 + ( 8*7), + 5 + ( 9*7), + 5 + (10*7), + 5 + (11*7), + 5 + (12*7), + 5 + (13*7), + 5 + (14*7), + 5 + (15*7), // server 16 }; #ifndef HBA_WRITE_DELAYS @@ -107,23 +107,29 @@ namespace LOFAR { 0x13, // PROTOCOL_C_END }; - uint8 HBAProtocolWrite::i2c_result[HBAProtocolWrite::RESULT_SIZE] - = { - // Expected protocol result (4 bytes) - 0x00, // PROTOCOL_WRITE_BYTE went OK + uint8 HBAProtocolWrite::i2c_result[HBAProtocolWrite::RESULT_SIZE] + = { + // Expected protocol result (4 bytes) + 0x00, // PROTOCOL_WRITE_BYTE went OK - 0xBB, // Read LED value - 0x00, // PROTOCOL_READ_BYTE went OK + 0xBB, // Read LED value + 0x00, // PROTOCOL_READ_BYTE went OK - 0x00, // PROTOCOL_C_END went OK - }; + 0x00, // PROTOCOL_C_END went OK + }; #else - uint8 HBAProtocolWrite::i2c_protocol[HBAProtocolWrite::PROTOCOL_SIZE/* 42 + 5 + 16 * 17 + 1 = 320 bytes*/] + uint8 HBAProtocolWrite::i2c_protocol[HBAProtocolWrite::PROTOCOL_SIZE/* 5 + 42 + 5 + 16 * 17 + 1 = 325 bytes*/] = { // Table 32 t/m 37 from LOFAR-ASTRON-MEM-175 (Revision 1.3) + 0x12, // PROTOCOL_C_WAIT, used to prevent power peeks. + 0x00, // <<< replace with data, wait byte 0 >>> + 0x00, // <<< replace with data, wait byte 1 >>> + 0x00, // <<< replace with data, wait byte 2 >>> + 0x00, // <<< replace with data, wait byte 3 >>> + // Table 32 // Instruct client to do a broadcast access to the 16 HBA delay servers (42 bytes) 0x0D, // PROTOCOL_C_WRITE_BLOCK_NO_CNT @@ -136,8 +142,8 @@ namespace LOFAR { 0, // Server X-delay register ID 0, // First server (initialization will add offset) 15, // Last server - 0xBB, // X-delay data for server 0 (offset = PROTOCOL_DELAY_OFFSET) - 0xBB, // Y-delay data for server 0 + 0xBB, // X-delay data for server 0 (offset = PROTOCOL_DELAY_OFFSET) + 0xBB, // Y-delay data for server 0 0xBB, // X-delay data for server 1 0xBB, // Y-delay data for server 1 0xBB, // X-delay data for server 2 @@ -182,15 +188,16 @@ namespace LOFAR { 4>>1, // Client i2c slave address 0, // Request register access command 4, // Count of number of data bytes to write - 0, // Server address 0 + 0, // Server address 0 3, // Payload length, including this octet 5, // Function: get word (document LOFAR-ASTRON-MEM-175 Revision 1.3 has an error here (value 2)) 0, // Server X-delay register ID - + // Table 35 // Wait to allow for the multicast to finish 0x12, // PROTOCOL_C_WAIT (5 bytes) - 0x00, 0xA8, 0x61, 0x00, // 0x0061A800 = 6.4e6 * 5ns = 32ms pause between write request register and read response register + + 0x00, 0xA8, 0x61, 0x00, // 0x0061A800 = 6.4e6 * 5ns = 32ms pause between write request register and read response register // 0x00, 0x09, 0x3D, 0x00, // 0x003D0900 = 4e6 * 5ns = 20ms pause between write request register and read response register // Table 36 @@ -201,28 +208,30 @@ namespace LOFAR { 4, // Count of number of data bytes to read (document LOFAR-ASTRON-MEM-175 has an error here (value 3)) // Repeat tables 34,35,36 fo servers 1 to 15 - 0x0D, 4>>1, 0, 4, 1, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 1 - 0x0D, 4>>1, 0, 4, 2, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 2 - 0x0D, 4>>1, 0, 4, 3, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 3 - 0x0D, 4>>1, 0, 4, 4, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 4 - 0x0D, 4>>1, 0, 4, 5, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 5 - 0x0D, 4>>1, 0, 4, 6, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 6 - 0x0D, 4>>1, 0, 4, 7, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 7 - 0x0D, 4>>1, 0, 4, 8, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 8 - 0x0D, 4>>1, 0, 4, 9, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 9 - 0x0D, 4>>1, 0, 4, 10, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 10 - 0x0D, 4>>1, 0, 4, 11, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 11 - 0x0D, 4>>1, 0, 4, 12, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 12 - 0x0D, 4>>1, 0, 4, 13, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 13 - 0x0D, 4>>1, 0, 4, 14, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 14 - 0x0D, 4>>1, 0, 4, 15, 3, 5, 0, 0x12, 0x00, 0x09, 0x3D, 0x00, 0x0E, 4>>1, 1, 4, // server 15 + 0x0D, 4>>1, 0, 4, 1, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 1 + 0x0D, 4>>1, 0, 4, 2, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 2 + 0x0D, 4>>1, 0, 4, 3, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 3 + 0x0D, 4>>1, 0, 4, 4, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 4 + 0x0D, 4>>1, 0, 4, 5, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 5 + 0x0D, 4>>1, 0, 4, 6, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 6 + 0x0D, 4>>1, 0, 4, 7, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 7 + 0x0D, 4>>1, 0, 4, 8, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 8 + 0x0D, 4>>1, 0, 4, 9, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 9 + 0x0D, 4>>1, 0, 4, 10, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 10 + 0x0D, 4>>1, 0, 4, 11, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 11 + 0x0D, 4>>1, 0, 4, 12, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 12 + 0x0D, 4>>1, 0, 4, 13, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 13 + 0x0D, 4>>1, 0, 4, 14, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 14 + 0x0D, 4>>1, 0, 4, 15, 3, 5, 0, 0x12, 0x00, 0xA8, 0x61, 0x00, 0x0E, 4>>1, 1, 4, // server 15 // Mark the end of the protocol list (1 byte) 0x13, // PROTOCOL_C_END }; - uint8 HBAProtocolWrite::i2c_result[HBAProtocolWrite::RESULT_SIZE/* 2 + 7*16 + 1 = 115 bytes*/] + uint8 HBAProtocolWrite::i2c_result[HBAProtocolWrite::RESULT_SIZE/* 3 + 7*16 + 1 = 116 bytes*/] = { + 0x00, // PROTOCOL_C_WAIT went OK + 0x00, // PROTOCOL_C_WRITE_BLOCK_NO_CNT went OK 0x00, // PROTOCOL_C_WAIT went OK @@ -236,21 +245,21 @@ namespace LOFAR { 0xBB, // Y-delay data from server X 0x00, // PROTOCOL_C_READ_BLOCK_NO_CNT went OK - 0, 0, 1 + 128, 3, 0xBB, 0xBB, 0, // server 1 result - 0, 0, 2 + 128, 3, 0xBB, 0xBB, 0, // server 2 result - 0, 0, 3 + 128, 3, 0xBB, 0xBB, 0, // server 3 result - 0, 0, 4 + 128, 3, 0xBB, 0xBB, 0, // server 4 result - 0, 0, 5 + 128, 3, 0xBB, 0xBB, 0, // server 5 result - 0, 0, 6 + 128, 3, 0xBB, 0xBB, 0, // server 6 result - 0, 0, 7 + 128, 3, 0xBB, 0xBB, 0, // server 7 result - 0, 0, 8 + 128, 3, 0xBB, 0xBB, 0, // server 8 result - 0, 0, 9 + 128, 3, 0xBB, 0xBB, 0, // server 9 result - 0, 0, 10 + 128, 3, 0xBB, 0xBB, 0, // server 10 result - 0, 0, 11 + 128, 3, 0xBB, 0xBB, 0, // server 11 result - 0, 0, 12 + 128, 3, 0xBB, 0xBB, 0, // server 12 result - 0, 0, 13 + 128, 3, 0xBB, 0xBB, 0, // server 13 result - 0, 0, 14 + 128, 3, 0xBB, 0xBB, 0, // server 14 result - 0, 0, 15 + 128, 3, 0xBB, 0xBB, 0, // server 15 result + 0x00, 0x00, 1 + 128, 3, 0xBB, 0xBB, 0x00, // server 1 result + 0x00, 0x00, 2 + 128, 3, 0xBB, 0xBB, 0x00, // server 2 result + 0x00, 0x00, 3 + 128, 3, 0xBB, 0xBB, 0x00, // server 3 result + 0x00, 0x00, 4 + 128, 3, 0xBB, 0xBB, 0x00, // server 4 result + 0x00, 0x00, 5 + 128, 3, 0xBB, 0xBB, 0x00, // server 5 result + 0x00, 0x00, 6 + 128, 3, 0xBB, 0xBB, 0x00, // server 6 result + 0x00, 0x00, 7 + 128, 3, 0xBB, 0xBB, 0x00, // server 7 result + 0x00, 0x00, 8 + 128, 3, 0xBB, 0xBB, 0x00, // server 8 result + 0x00, 0x00, 9 + 128, 3, 0xBB, 0xBB, 0x00, // server 9 result + 0x00, 0x00, 10 + 128, 3, 0xBB, 0xBB, 0x00, // server 10 result + 0x00, 0x00, 11 + 128, 3, 0xBB, 0xBB, 0x00, // server 11 result + 0x00, 0x00, 12 + 128, 3, 0xBB, 0xBB, 0x00, // server 12 result + 0x00, 0x00, 13 + 128, 3, 0xBB, 0xBB, 0x00, // server 13 result + 0x00, 0x00, 14 + 128, 3, 0xBB, 0xBB, 0x00, // server 14 result + 0x00, 0x00, 15 + 128, 3, 0xBB, 0xBB, 0x00, // server 15 result // Marks the end of the protocol list (1 byte) 0x00, // PROTOCOL_C_END went OK @@ -309,7 +318,7 @@ void HBAProtocolWrite::sendrequest() setContinue(true); return; } - + if ((regStates.hbaprotocol().get(global_blp * N_POL) != RTC::RegisterState::WRITE) && (regStates.hbaprotocol().get(global_blp * N_POL + 1) != RTC::RegisterState::WRITE)) { regStates.hbaprotocol().unmodified(global_blp * N_POL); @@ -330,14 +339,14 @@ void HBAProtocolWrite::sendrequest() neverDeleteData); // add extra wait to prevent power peaks when switching - // 1 tick = 5ns, 16000000 (80ms), 600000 (3ms) - uint32 wait = 16000000 + (600000 * global_blp); - LOG_DEBUG_STR(formatString("HBAPotocolWrite add wait %f sec", wait * (1./200e6))); - memcpy(i2c_protocol + PROTOCOL_WAIT_OFFSET, &wait, 4); + // 200MHz, 1 tick = 5ns, 600000 = 3ms + uint32 wait = 600000 * global_blp; + LOG_INFO_STR(formatString("HBAPotocolWrite add wait %f sec for global_blp %d", wait * (1./200e6), global_blp)); + memcpy(i2c_protocol + PROTOCOL_WAIT_OFFSET_1, &wait, 4); delays(Range::all(), 0) = Cache::getInstance().getBack().getHBASettings()()(global_blp * N_POL, Range::all()); delays(Range::all(), 1) = Cache::getInstance().getBack().getHBASettings()()(global_blp * N_POL + 1, Range::all()); - + // copy set delays to i2c_result which is the expected result uint8* cur = i2c_result + RESULT_DELAY_OFFSET; for (int elem = 0; elem < N_HBA_ELEM_PER_TILE; elem++){ @@ -355,13 +364,13 @@ void HBAProtocolWrite::sendrequest() EPARcuProtocolEvent rcuprotocol; rcuprotocol.hdr.set(MEPHeader::RCU_PROTOCOLY_HDR, 1 << (getCurrentIndex() / N_WRITES), MEPHeader::WRITE, sizeof(i2c_protocol)); rcuprotocol.protocol.setBuffer(i2c_protocol, sizeof(i2c_protocol)); - -#if 0 + +#if 0 string tmpbuf; hexdump (tmpbuf, i2c_protocol, sizeof(i2c_protocol)); LOG_INFO_STR("HBA WRITE: " << tmpbuf); -#endif - +#endif + m_hdr = rcuprotocol.hdr; // remember header to match with ack getBoardPort().send(rcuprotocol); } @@ -376,13 +385,13 @@ void HBAProtocolWrite::sendrequest() uint8 clear[RESULT_SIZE]; memset(clear, 0xBB, RESULT_SIZE); // clear result rcuresultwrite.payload.setBuffer(clear, RESULT_SIZE); - -#if 0 + +#if 0 string tmpbuf; hexdump (tmpbuf, clear, sizeof(clear)); LOG_INFO_STR("HBA RESULT WRITE: " << tmpbuf); -#endif - +#endif + m_hdr = rcuresultwrite.hdr; // remember header to match with ack getBoardPort().send(rcuresultwrite); } @@ -414,7 +423,7 @@ GCFEvent::TResult HBAProtocolWrite::handleack(GCFEvent& event, GCFPortInterface& return GCFEvent::NOT_HANDLED; } - if ((getCurrentIndex() % N_WRITES) == 1) { + if ((getCurrentIndex() % N_WRITES) == 1) { // Mark modification as applied when write of RCU result register has completed Cache::getInstance().getState().hbaprotocol().schedule_wait2read(global_blp * N_POL); Cache::getInstance().getState().hbaprotocol().schedule_wait2read(global_blp * N_POL + 1); diff --git a/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.h b/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.h index c97e6b1cceafa73704c6a36bd009a443df95fdd7..2cbe71ff561fb26699c3e14de084182c22358f0c 100644 --- a/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.h +++ b/MAC/APL/PIC/RSP_Driver/src/HBAProtocolWrite.h @@ -71,18 +71,19 @@ namespace LOFAR { #define HBA_WRITE_DELAYS #ifdef HBA_WRITE_DELAYS - static const int PROTOCOL_SIZE = 320; - static const int RESULT_SIZE = 115; - - static const int PROTOCOL_WAIT_OFFSET = 43; // offset of wait settings in i2c_protocol - static const int PROTOCOL_DELAY_OFFSET = 10; // offset of delay settings in i2c_protocol - static const int RESULT_DELAY_OFFSET = 6; // offset of delay settings in i2c_result - static const int RESULT_DELAY_STRIDE = 7; // nof bytes to next pair of X,Y delays + static const int PROTOCOL_SIZE = 325; + static const int RESULT_SIZE = 116; + + static const int PROTOCOL_WAIT_OFFSET_1 = 1; // offset of wait settings in i2c_protocol + static const int PROTOCOL_WAIT_OFFSET_2 = 48; // offset of wait settings in i2c_protocol + static const int PROTOCOL_DELAY_OFFSET = 15; // offset of delay settings in i2c_protocol + static const int RESULT_DELAY_OFFSET = 7; // offset of delay settings in i2c_result + static const int RESULT_DELAY_STRIDE = 7; // nof bytes to next pair of X,Y delays #else - static const int PROTOCOL_SIZE = 8; - static const int RESULT_SIZE = 4; - static const int PROTOCOL_LED_OFFSET = 3; - static const int RESULT_LED_OFFSET = 1; + static const int PROTOCOL_SIZE = 8; + static const int RESULT_SIZE = 4; + static const int PROTOCOL_LED_OFFSET = 3; + static const int RESULT_LED_OFFSET = 1; #endif static int i2c_protocol_patch_indices[]; @@ -100,5 +101,5 @@ namespace LOFAR { }; }; }; - + #endif /* HBAPROTOCOLWRITE_H_ */ diff --git a/MAC/APL/PIC/RSP_Driver/src/HBAResultRead.cc b/MAC/APL/PIC/RSP_Driver/src/HBAResultRead.cc index 9ed44cafa1f24090f0556099342a7a50ffe9b6c9..40e53b2d5b09d5961ad86fb57633a613c4bf817b 100644 --- a/MAC/APL/PIC/RSP_Driver/src/HBAResultRead.cc +++ b/MAC/APL/PIC/RSP_Driver/src/HBAResultRead.cc @@ -83,7 +83,7 @@ GCFEvent::TResult HBAResultRead::handleack(GCFEvent& event, GCFPortInterface& /* LOG_WARN("HBAResultRead::handleack:: unexpected ack"); return GCFEvent::NOT_HANDLED; } - + EPARcuResultEvent ack(event); uint8 global_blp = (getBoardId() * NR_BLPS_PER_RSPBOARD) + getCurrentIndex(); @@ -126,7 +126,7 @@ GCFEvent::TResult HBAResultRead::handleack(GCFEvent& event, GCFPortInterface& /* Cache::getInstance().getState().hbaprotocol().read_ack(global_blp * N_POL); Cache::getInstance().getState().hbaprotocol().read_ack(global_blp * N_POL + 1); } else { - LOG_WARN_STR("HBAResultRead: unexpected I2C result response for element(s):" << faultyElements << + LOG_WARN_STR("HBAResultRead: unexpected I2C result response for element(s):" << faultyElements << " of antenna " << (int)(global_blp)); Cache::getInstance().getState().hbaprotocol().read_error(global_blp * N_POL); Cache::getInstance().getState().hbaprotocol().read_error(global_blp * N_POL + 1); diff --git a/MAC/APL/PIC/RSP_Driver/src/RCUProtocolWrite.cc b/MAC/APL/PIC/RSP_Driver/src/RCUProtocolWrite.cc index 109b2b766ca3c90491959afde97ef61be33ec836..6edaad7eae7e52b90da972b20542f9a9ee65eb5c 100644 --- a/MAC/APL/PIC/RSP_Driver/src/RCUProtocolWrite.cc +++ b/MAC/APL/PIC/RSP_Driver/src/RCUProtocolWrite.cc @@ -50,7 +50,7 @@ namespace LOFAR { 0x00, // <<< replace with data, wait byte 0 >>> 0x00, // <<< replace with data, wait byte 1 >>> 0x00, // <<< replace with data, wait byte 2 >>> - 0x00, // <<< replace with data, wait byte 3 >>> + 0x00, // <<< replace with data, wait byte 3 >>> 0x0F, // PROTOCOL_C_SEND_BLOCK 0x01, // I2C address for RCU 0x03, // size @@ -63,14 +63,14 @@ namespace LOFAR { 0x13, // PROTOCOL_C_END }; - uint8 RCUProtocolWrite::i2c_protocol_read[] = { + uint8 RCUProtocolWrite::i2c_protocol_read[] = { 0x10, // PROTOCOL_C_RECEIVE_BLOCK 0x01, // I2C adress for RCU 0x03, // requested size 0x13, // PROTOCOL_C_END }; - uint8 RCUProtocolWrite::i2c_result_write[] = { + uint8 RCUProtocolWrite::i2c_result_write[] = { 0x00, // PROTOCOL_C_WAIT OK 0x00, // PROTOCOL_C_SEND_BLOCK OK 0xAA, // <<< replace with expected data >>> @@ -80,7 +80,7 @@ namespace LOFAR { 0x00, // PROTOCOL_C_END OK }; - uint8 RCUProtocolWrite::i2c_result_read[] = { + uint8 RCUProtocolWrite::i2c_result_read[] = { 0xAA, // <<< replace with expected data >>> 0xAA, // <<< replace with expected data >>> 0xAA, // <<< replace with expected data >>> @@ -143,19 +143,19 @@ void RCUProtocolWrite::sendrequest() if (writeCmdRequested) { // reverse and copy control bytes into i2c_protocol_write RCUSettings::Control& rcucontrol = Cache::getInstance().getBack().getRCUSettings()()((global_rcu)); - + // add waits while turning on hbas to reduce power peaks. // if RCU enable changed or rcumode changed // if rcumode > 0 if (rcucontrol.isModeModified()) { // wait between two RCUs is set to maximum, so that an international station // running on 160MHz clock can finisch the job in 1 second. - // in clock ticks, 1500000 = 7.5msec on 200MHz, 9.4msec on 160MHz - uint32 wait = 660000 * global_rcu; + // in clock ticks, 600000 = 3msec on 200MHz + uint32 wait = 600000 * global_rcu; LOG_DEBUG_STR(formatString("RCUProtocolWrite add wait rcu %d = %f sec", global_rcu, wait * (1./200e6))); memcpy(i2c_protocol_write+1, &wait, 4); } - + uint32 control = htonl(rcucontrol.getRaw()); memcpy(i2c_protocol_write+8, &control, 3); @@ -230,10 +230,10 @@ GCFEvent::TResult RCUProtocolWrite::handleack(GCFEvent& event, GCFPortInterface& if ((getCurrentIndex() % N_WRITES) == 1) { // Mark modification as applied when write of RCU result register has completed if (m_hdr.m_fields.payload_length == RESULT_WRITE_SIZE) { - Cache::getInstance().getState().rcuprotocol().schedule_wait1read(global_rcu); + Cache::getInstance().getState().rcuprotocol().schedule_wait2read(global_rcu); } else { - Cache::getInstance().getState().rcuread().schedule_wait1read(global_rcu); + Cache::getInstance().getState().rcuread().schedule_wait2read(global_rcu); } } diff --git a/MAC/APL/PIC/RSP_Driver/src/RCUWrite.cc b/MAC/APL/PIC/RSP_Driver/src/RCUWrite.cc index 3583b103428f87d865dd2668b04f6fb17f878faa..beb524875deaf3e000065c52fc974a3df308f3fe 100644 --- a/MAC/APL/PIC/RSP_Driver/src/RCUWrite.cc +++ b/MAC/APL/PIC/RSP_Driver/src/RCUWrite.cc @@ -35,8 +35,11 @@ using namespace LOFAR; using namespace RSP; using namespace EPA_Protocol; + +#define N_WRITES 2 // 2 writes, one for disable rcu and setdelay, one for enable rcu if requested + RCUWrite::RCUWrite(GCFPortInterface& board_port, int board_id) - : SyncAction(board_port, board_id, NR_BLPS_PER_RSPBOARD) + : SyncAction(board_port, board_id, NR_BLPS_PER_RSPBOARD * N_WRITES) { memset(&m_hdr, 0, sizeof(MEPHeader)); doAtInit(); // needed to enable/disable RCU's during initialization @@ -49,7 +52,7 @@ RCUWrite::~RCUWrite() void RCUWrite::sendrequest() { - uint8 global_blp = (getBoardId() * NR_BLPS_PER_RSPBOARD) + getCurrentIndex(); + uint8 global_blp = (getBoardId() * NR_BLPS_PER_RSPBOARD) + (getCurrentIndex() / N_WRITES); // skip update if the neither of the RCU's settings have been modified if (RTC::RegisterState::WRITE != Cache::getInstance().getState().rcusettings().get(global_blp * 2) @@ -68,12 +71,25 @@ void RCUWrite::sendrequest() LOG_DEBUG(formatString("%d.Y control=0x%08x", global_blp, y.getRaw())); EPARcuSettingsEvent rcusettings; - rcusettings.hdr.set(MEPHeader::RCU_SETTINGS_HDR, 1 << getCurrentIndex()); // also sets payload_length + rcusettings.hdr.set(MEPHeader::RCU_SETTINGS_HDR, 1 << (getCurrentIndex() / N_WRITES)); // also sets payload_length rcusettings.ap = EPA_Protocol::RCUHandler(); - rcusettings.ap.input_delay_x = x.getDelay(); - rcusettings.ap.enable_x = x.getEnable(); - rcusettings.ap.input_delay_y = y.getDelay(); - rcusettings.ap.enable_y = y.getEnable(); + + // new delay is active after datastream restart + switch (getCurrentIndex() % N_WRITES) { + case 0: { + rcusettings.ap.input_delay_x = x.getDelay(); + rcusettings.ap.enable_x = 0; + rcusettings.ap.input_delay_y = y.getDelay(); + rcusettings.ap.enable_y = 0; + } break; + + case 1: { + rcusettings.ap.input_delay_x = x.getDelay(); + rcusettings.ap.enable_x = x.getEnable(); + rcusettings.ap.input_delay_y = y.getDelay(); + rcusettings.ap.enable_y = y.getEnable(); + } break; + } m_hdr = rcusettings.hdr; getBoardPort().send(rcusettings); @@ -91,10 +107,10 @@ GCFEvent::TResult RCUWrite::handleack(GCFEvent& event, GCFPortInterface& /*port* LOG_WARN("RCUWrite::handleack:: unexpected ack"); return GCFEvent::NOT_HANDLED; } - + EPAWriteackEvent ack(event); - uint8 global_blp = (getBoardId() * NR_BLPS_PER_RSPBOARD) + getCurrentIndex(); + uint8 global_blp = (getBoardId() * NR_BLPS_PER_RSPBOARD) + (getCurrentIndex() / N_WRITES); if (!ack.hdr.isValidAck(m_hdr)) { @@ -104,8 +120,10 @@ GCFEvent::TResult RCUWrite::handleack(GCFEvent& event, GCFPortInterface& /*port* return GCFEvent::NOT_HANDLED; } - Cache::getInstance().getState().rcusettings().write_ack(global_blp * 2); - Cache::getInstance().getState().rcusettings().write_ack(global_blp * 2 + 1); + if ((getCurrentIndex() % N_WRITES) == 1) { + Cache::getInstance().getState().rcusettings().write_ack(global_blp * 2); + Cache::getInstance().getState().rcusettings().write_ack(global_blp * 2 + 1); + } return GCFEvent::HANDLED; } diff --git a/MAC/APL/PIC/RSP_Driver/src/Scheduler.cc b/MAC/APL/PIC/RSP_Driver/src/Scheduler.cc index d7d47622f41ba37126281b6a86169067bb67b43f..cddd07f4dbef7e76aeaea9c9262b53f1b1340a23 100644 --- a/MAC/APL/PIC/RSP_Driver/src/Scheduler.cc +++ b/MAC/APL/PIC/RSP_Driver/src/Scheduler.cc @@ -115,7 +115,8 @@ GCFEvent::TResult Scheduler::run(GCFEvent& event, GCFPortInterface& /*port*/) if (timeout->usec >= 1e6-1000) { topofsecond++; // round to next whole second } - if (timeout->usec > 2000 && timeout->usec < 1e6 - 2000) { + //if (timeout->usec > 2000 && timeout->usec < 1e6 - 2000) { + if (timeout->usec > 15000 && timeout->usec < 1e6 - 15000) { // changed to 15000, because itsPPSdelay in RSPDriver is added to sync to hardware LOG_WARN_STR("Scheduler time too far off from top off second: usec=" << timeout->usec); } setCurrentTime(topofsecond, 0); @@ -135,7 +136,7 @@ GCFEvent::TResult Scheduler::run(GCFEvent& event, GCFPortInterface& /*port*/) // // dispatch (event, port) // -// Dispatch the(ack) message to the right SyncAction and wake up the next SyncAction in the queue +// Dispatch the(ack) message to the right SyncAction and wake up the next SyncAction in the queue // when the current SyncAction is complete. GCFEvent::TResult Scheduler::dispatch(GCFEvent& event, GCFPortInterface& port) { @@ -148,7 +149,7 @@ GCFEvent::TResult Scheduler::dispatch(GCFEvent& event, GCFPortInterface& port) // has not yet reached its 'final' state. vector<SyncAction*>::iterator sa; int i(0); - // note: m_syncaction = map< GCFPortInterface*, std::vector<SyncAction*> > + // note: m_syncaction = map< GCFPortInterface*, std::vector<SyncAction*> > for (sa = m_syncactions[&port].begin(); sa != m_syncactions[&port].end(); sa++, i++) { if (!(*sa)->hasCompleted()) { // stil busy @@ -165,7 +166,7 @@ GCFEvent::TResult Scheduler::dispatch(GCFEvent& event, GCFPortInterface& port) else { // this SyncAction was ready, pass a timer event to the next action. sync_completed = true; - current_event = &timer; + current_event = &timer; } } itsSyncErrors[&port] |= (*sa)->hasErrors(); @@ -191,7 +192,7 @@ bool Scheduler::syncHasCompleted() { for (map< GCFPortInterface*, bool >::iterator it = m_sync_completed.begin(); it != m_sync_completed.end(); it++) { - if (!(*it).second) { + if (!(*it).second) { return(false); } } @@ -317,8 +318,8 @@ void Scheduler::enter(Ptr<Command> command, QueueID queue, bool immediateApplyAl Timestamp scheduled_time = command->getTimestamp(); // process READ commmand immediately if that is possible, - if ((command->getOperation() == Command::READ) && - (scheduled_time == Timestamp(0,0)) && + if ((command->getOperation() == Command::READ) && + (scheduled_time == Timestamp(0,0)) && (command->readFromCache()) && (queue != Scheduler::PERIODIC)) { LOG_INFO_STR("Applying command " << command->name() << " immediately"); @@ -344,13 +345,12 @@ void Scheduler::enter(Ptr<Command> command, QueueID queue, bool immediateApplyAl } /* determine at which time the command can actually be carried out */ - if (scheduled_time.sec() < m_current_time.sec() + SCHEDULING_DELAY) { - if (scheduled_time.sec() > 0) { // filter Timestamp(0,0) case + if (scheduled_time.sec() < m_current_time.sec() + (long)SCHEDULING_DELAY) { + if ((scheduled_time.sec() > 0L) && (m_current_time.sec() - scheduled_time.sec()) != 0L) { // filter Timestamp(0,0) case LOG_WARN(formatString("command %s missed deadline by %d seconds", command->name().c_str(), m_current_time.sec() - scheduled_time.sec())); } - scheduled_time = m_current_time + (long)SCHEDULING_DELAY; } @@ -424,7 +424,7 @@ void Scheduler::scheduleCommands() } /* detect late commands, but just execute them */ - if (command->getTimestamp() <= m_current_time + (long)(scheduling_offset - SYNC_INTERVAL_INT)) { + if (command->getTimestamp() < m_current_time + (long)(scheduling_offset - SYNC_INTERVAL_INT)) { LOG_WARN_STR("command '" << command->name() << "' is late, timestamp=" << command->getTimestamp() << ", current_time=" << m_current_time); } @@ -455,7 +455,7 @@ void Scheduler::scheduleCommands() } /* detect late commands, but just execute them */ - if (command->getTimestamp() <= m_current_time + (long)(scheduling_offset - SYNC_INTERVAL_INT)) { + if (command->getTimestamp() < m_current_time + (long)(scheduling_offset - SYNC_INTERVAL_INT)) { LOG_WARN_STR("periodic command '" << command->name() << "' is late, timestamp=" << command->getTimestamp() << ", current_time=" << m_current_time); } @@ -529,7 +529,7 @@ void Scheduler::initiateSync(GCFEvent& event) itsSyncErrors[iter->first] = false; // also mark all commands as not completed yet. - for (vector<SyncAction*>::iterator sa = iter->second.begin(); sa != iter->second.end(); sa++) { + for (vector<SyncAction*>::iterator sa = iter->second.begin(); sa != iter->second.end(); sa++) { (*sa)->setCompleted(false); } @@ -615,12 +615,12 @@ void Scheduler::completeCommands() while (!m_done_queue.empty()) { Ptr<Command> command = m_done_queue.top(); m_done_queue.pop(); - - // If command->getPort() = 0, an ack for this command is already send in the enter function + + // If command->getPort() = 0, an ack for this command is already send in the enter function if (command->getPort() != 0) { command->complete(Cache::getInstance().getFront()); } - + // re-timestamp periodic commands for the next period if (command->getPeriod()) { // Set the next time at which the periodic command should be executed. @@ -633,7 +633,7 @@ void Scheduler::completeCommands() // // To correctly compute the next time at which a periodic command should execute, take // the absolute current time and add the period, but make sure the periodic command - // continues to be executed on the grid defined by the period. E.g. if a command is + // continues to be executed on the grid defined by the period. E.g. if a command is // executed every 4 seconds starting on second 1, so 1,5,9, etc and some PPSes are missed // lets say PPS 10,11,12,13 (and current time is 13) then it should continue at time 17. // diff --git a/MAC/APL/PIC/RSP_Driver/src/SerdesRead.cc b/MAC/APL/PIC/RSP_Driver/src/SerdesRead.cc index c8f8f5721727dd7aba660e292ba4b6ef4cac2ebc..e4446eabdac22f1fd382ed5ba7ed96e705772024 100644 --- a/MAC/APL/PIC/RSP_Driver/src/SerdesRead.cc +++ b/MAC/APL/PIC/RSP_Driver/src/SerdesRead.cc @@ -76,9 +76,11 @@ void SerdesRead::sendrequest() setContinue(true); setFinished(); SerdesBuffer& rdBuf = Cache::getInstance().getBack().getSdsReadBuffer(getBoardId()); - string hd; +#if 0 + string hd; hexdump (hd, rdBuf.getBufferPtr(), dataOffset+1); LOG_INFO_STR(hd); +#endif return; } diff --git a/MAC/APL/PIC/RSP_Driver/src/SetHBACmd.cc b/MAC/APL/PIC/RSP_Driver/src/SetHBACmd.cc index f1ad9c67a5be1231ee84dafc89d9ea1d91e8346b..8415048f2b0e27f750ed68d20ab3df11122f8e08 100644 --- a/MAC/APL/PIC/RSP_Driver/src/SetHBACmd.cc +++ b/MAC/APL/PIC/RSP_Driver/src/SetHBACmd.cc @@ -54,7 +54,7 @@ void SetHBACmd::ack(CacheBuffer& /*cache*/) ack.timestamp = getTimestamp(); ack.status = RSP_SUCCESS; - + getPort()->send(ack); } @@ -62,7 +62,7 @@ void SetHBACmd::apply(CacheBuffer& cache, bool setModFlag) { // someone else using the I2C bus? I2Cuser busUser = cache.getI2Cuser(); - LOG_INFO_STR("SetHBA::apply : " << ((busUser == NONE) ? "NONE" : ((busUser == HBA) ? "HBA" : "RCU"))); + LOG_DEBUG_STR("SetHBA::apply : " << ((busUser == NONE) ? "NONE" : ((busUser == HBA) ? "HBA" : "RCU"))); if (busUser != NONE && busUser != HBA) { postponeExecution(true); return; @@ -78,18 +78,27 @@ void SetHBACmd::apply(CacheBuffer& cache, bool setModFlag) bool delays_changed; for (int cache_rcu = 0; cache_rcu < StationSettings::instance()->nrRcus(); cache_rcu++) { if (m_event->rcumask.test(cache_rcu)) { // check if rcu is selected - + + if (cache.getRCUSettings()()(cache_rcu).getMode() < 5) + continue; // et only if in HBA mode + // check if changed delays_changed = false; - for (int i = 0; i < 16; ++i) { + + //LOG_INFO_STR("new hba delays=" << cache.getHBASettings()()(cache_rcu, Range::all())); + //LOG_INFO_STR("old hba delays=" << m_event->settings()(event_rcu, Range::all())); + + for (int i = 0; i < 16; ++i) { if (cache.getHBASettings()()(cache_rcu, Range::all())(i) != m_event->settings()(event_rcu, Range::all())(i) ) { delays_changed = true; } } if (!delays_changed) { - LOG_DEBUG_STR("Skip updating rcu " << cache_rcu << ", value not changed"); + LOG_DEBUG_STR("Skip updating rcu " << cache_rcu << ", value not changed"); } - + + //delays_changed = true; + cache.getHBASettings()()(cache_rcu, Range::all()) = m_event->settings()(event_rcu, Range::all()); if (setModFlag && delays_changed) { cache.getCache().getState().hbaprotocol().write(cache_rcu); @@ -117,11 +126,11 @@ void SetHBACmd::setTimestamp(const Timestamp& timestamp) bool SetHBACmd::validate() const { LOG_INFO_STR( - "validate-> rcus=" << m_event->rcumask.count() - << " dims=" << m_event->settings().dimensions() + "validate-> rcus=" << m_event->rcumask.count() + << " dims=" << m_event->settings().dimensions() << " ext_firt=" << m_event->settings().extent(firstDim) << " elements=" << m_event->settings().extent(secondDim) ); - + return ((m_event->rcumask.count() <= (unsigned int)StationSettings::instance()->nrRcus()) && (2 == m_event->settings().dimensions()) && (m_event->rcumask.count() == (unsigned int)m_event->settings().extent(firstDim)) // check number off selected rcus diff --git a/MAC/APL/PIC/RSP_Driver/src/SetRCUCmd.cc b/MAC/APL/PIC/RSP_Driver/src/SetRCUCmd.cc index 939cad40b2fe95460d364f8b7ecfa1805f6ed998..e95fb1f26794639b2e22833862dc8310344765a9 100644 --- a/MAC/APL/PIC/RSP_Driver/src/SetRCUCmd.cc +++ b/MAC/APL/PIC/RSP_Driver/src/SetRCUCmd.cc @@ -90,16 +90,19 @@ void SetRCUCmd::apply(CacheBuffer& cache, bool setModFlag) // Apply delays and attenuation when mode was changed. if (m_event->settings()(eventRcu).isModeModified()) { - mode = m_event->settings()(eventRcu).getMode(); - // if mode changed be sure RCU is enabled, is needed to reduce poweron current on hba's - /* - if (mode > 0) { - cache.getRCUSettings()()(cache_rcu).setEnable(1); + + mode = m_event->settings()(eventRcu).getMode(); + if (m_event->settings()(eventRcu).getMode() == -1) { + LOG_INFO_STR("RCU " << eventRcu << ", mode = -1, bad RCU readout, use 0 instead"); + mode = 0; } - else { - cache.getRCUSettings()()(cache_rcu).setEnable(0); + + // clear HBA delays if mode < than 5 is selected + if (mode < 5) { + cache.getHBASettings()()(cache_rcu, Range::all()) = 0; + cache.getHBAReadings()()(cache_rcu, Range::all()) = 0; } - */ + cache.getRCUSettings()()(cache_rcu).setDelay( (uint8) ((delayStep/2.0 + cableSettings->getDelay(cache_rcu, mode)) / delayStep)); diff --git a/MAC/APL/PIC/RSP_Driver/src/rspctl.cc b/MAC/APL/PIC/RSP_Driver/src/rspctl.cc index 6a700cdb9538df6a5bccc68eaa839f615aab724b..6f8912cd98b52b9fb07e3c814b57e5148eaf260d 100644 --- a/MAC/APL/PIC/RSP_Driver/src/rspctl.cc +++ b/MAC/APL/PIC/RSP_Driver/src/rspctl.cc @@ -538,6 +538,211 @@ GCFEvent::TResult RCUCommand::ack(GCFEvent& e) return GCFEvent::HANDLED; } +// Easy command set most settings with one command +ModeCommand::ModeCommand(GCFPortInterface& port) : + Command (port), + itsMode (0), + itsClock (0), + itsStage (0) +{ +} + +void ModeCommand::send() +{ + switch (itsStage) { + case 0: { + RSPGetclockEvent getclock; + getclock.timestamp = Timestamp(0,0); + getclock.cache = true; + m_rspport.send(getclock); + } break; + + case 1: { + RSPSetclockEvent setclock; + setclock.timestamp = Timestamp(0,0); + if (itsClock == 160) + setclock.clock = 200; + else + setclock.clock = 160; + m_rspport.send(setclock); + logMessage(cout, formatString("Set clock to %dMHz", setclock.clock)); + } break; + + case 2: { + RSPSetrcuEvent setrcu; + setrcu.timestamp = Timestamp(0,0); + setrcu.rcumask = getRCUMask(); + + setrcu.settings().resize(1); + + setrcu.settings()(0).setMode((RCUSettings::Control::RCUMode)itsMode); + logMessage(cout,formatString("Set rcumode to %d", itsMode)); + + if (itsMode > 0) { + setrcu.settings()(0).setEnable(true); + logMessage(cout, "Enable inputs from RCU's"); + } else { + setrcu.settings()(0).setEnable(false); + logMessage(cout, "Disable inputs from RCU's"); + } + m_rspport.send(setrcu); + } break; + + case 3: { + RSPSetbypassEvent request; + + request.timestamp = Timestamp(0,0); + request.rcumask = getRCUMask(); + request.settings().resize(1); + bool siOn; + if (itsMode == 5) { + logMessage(cout, "Enable Spectral inversion"); + siOn = true; + } + else { + logMessage(cout, "Disable Spectral inversion"); + siOn = false; + } + request.settings()(0).setXSI(siOn); + request.settings()(0).setYSI(siOn); + + m_rspport.send(request); + } break; + + case 4: { + RSPSethbaEvent sethba; + sethba.timestamp = Timestamp(0,0); + sethba.rcumask = getRCUMask(); + sethba.settings().resize(sethba.rcumask.count(), N_HBA_ELEM_PER_TILE); + sethba.settings() = 0; // discharge hba elements for one second. + logMessage(cout, "Discharge HBA elements for 1 second"); + m_rspport.send(sethba); + } break; + + case 5: { + RSPSethbaEvent sethba; + sethba.timestamp = Timestamp(0,0); + sethba.rcumask = getRCUMask(); + sethba.settings().resize(sethba.rcumask.count(), N_HBA_ELEM_PER_TILE); + sethba.settings() = 253; + logMessage(cout, "Set HBA delays to 253"); + m_rspport.send(sethba); + } break; + + } +} + +GCFEvent::TResult ModeCommand::ack(GCFEvent& e) +{ + GCFEvent::TResult status = GCFEvent::HANDLED; + + switch (e.signal) { + case RSP_GETCLOCKACK: { + RSPGetclockackEvent ack(e); + if (RSP_SUCCESS != ack.status) { + if (ack.status == RSP_BUSY) { + logMessage(cerr,"Error: driver busy."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + else { + logMessage(cerr,"Error: RSP_GETCLOCK command failed."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + } + else { + itsClock = ack.clock; + logMessage(cout,formatString("Sample frequency: clock=%dMHz", ack.clock)); + + ++itsStage; + if ((itsClock == 160) && (itsMode == 6)) + ++itsStage; // No clock change needed + + if ((itsClock == 200) && (itsMode != 6)) + ++itsStage; // No clock change needed + send(); + } + } break; + + case RSP_SETCLOCKACK: { + RSPSetclockackEvent ack(e); + if (RSP_SUCCESS != ack.status) { + if (ack.status == RSP_BUSY) { + logMessage(cerr,"Error: clock NOT set, driver busy."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + else { + logMessage(cerr,"Error: RSP_SETCLOCK command failed."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + } + logMessage(cout,"Wait 30 seconds until clock is changed"); + sleep(30); + ++itsStage; + send(); + } break; + + case RSP_SETRCUACK: { + RSPSetrcuackEvent ack(e); + if (ack.status != RSP_SUCCESS) { + logMessage(cerr,"Error: RSP_SETRCU command failed."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + ++itsStage; + send(); + } break; + + case RSP_SETBYPASSACK: { + RSPSetbypassackEvent ack(e); + + std::ostringstream msg; + msg << "setSIack.timestamp=" << ack.timestamp; + logMessage(cout, msg.str()); + + if (ack.status != RSP_SUCCESS) { + logMessage(cerr, "Error: RSP_SETSI command failed."); + rspctl_exit_code = EXIT_FAILURE; + GCFScheduler::instance()->stop(); + } + + if (itsMode < 5) { + GCFScheduler::instance()->stop(); + } + else { + logMessage(cout,formatString("rcumode > 5 (%d), send hba delay", itsMode)); + ++itsStage; + send(); + } + } break; + + case RSP_SETHBAACK: { + RSPSethbaackEvent ack(e); + if (ack.status != RSP_SUCCESS) { + logMessage(cerr,"Error: RSP_SETHBA command failed."); + rspctl_exit_code = EXIT_FAILURE; + } + if (itsStage < 5) { + sleep(1); + ++itsStage; + send(); + } + else { + GCFScheduler::instance()->stop(); + } + } break; + + default: { + status = GCFEvent::NOT_HANDLED; + } break; + + } // switch + + return status; +} // Swap X Y on RCU SWAPXYCommand::SWAPXYCommand(GCFPortInterface& port) : Command(port) @@ -715,7 +920,7 @@ GCFEvent::TResult BitmodeCommand::ack(GCFEvent& e) return status; } -// set subbands +// set subbands SDOCommand::SDOCommand(GCFPortInterface& port) : Command (port), itsBitsPerSample(0) @@ -749,11 +954,11 @@ void SDOCommand::send() setsdo.rcumask = getRCUMask(); logMessage(cerr,formatString("rcumask.count()=%d",setsdo.rcumask.count())); - + int MAX_SDO_SUBBANDS_PER_PLANE = 36; int MAX_SDO_PLANES = 4; int MAX_SDO_SUBBANDS = MAX_SDO_SUBBANDS_PER_PLANE * MAX_SDO_PLANES; - + if (static_cast<int>(itsSubbandlist.size()) > MAX_SDO_SUBBANDS) { logMessage(cerr,formatString("Error: too many subbands selected max=%d", MAX_SDO_SUBBANDS)); rspctl_exit_code = EXIT_FAILURE; @@ -803,7 +1008,7 @@ GCFEvent::TResult SDOCommand::ack(GCFEvent& e) itsBitsPerSample = n_bits_per_sample; send(); // bits per sample received get selected subbands. } break; - + case RSP_GETSDOACK: { RSPGetsdoackEvent ack(e); bitset<MAX_RCUS> mask = getRCUMask(); @@ -847,7 +1052,7 @@ GCFEvent::TResult SDOCommand::ack(GCFEvent& e) status = GCFEvent::NOT_HANDLED; break; } - + if (e.signal != RSP_GETSDOMODEACK) { GCFScheduler::instance()->stop(); } @@ -916,7 +1121,7 @@ GCFEvent::TResult SDOmodeCommand::ack(GCFEvent& e) break; case 3: cout << formatString("RSP[%02u]: 16/8/5/4 : %2d\n", rsp, ack.bits_per_sample[rsp]); - break; + break; default: break; } } @@ -1150,7 +1355,7 @@ GCFEvent::TResult RSUCommand::ack(GCFEvent& e) logMessage(cerr,"Error: RSP_SETRSU command failed."); rspctl_exit_code = EXIT_FAILURE; } - + } } } @@ -3525,7 +3730,7 @@ GCFEvent::TResult RSPCtl::doCommand(GCFEvent& e, GCFPortInterface& port) case RSP_GETSDOMODEACK: case RSP_SETSDOACK: case RSP_GETSDOACK: - + status = itsCommand->ack(e); // handle the acknowledgement gClockChanged = false; gBitmodeChanged = false; @@ -3639,6 +3844,12 @@ static void usage(bool exportMode) cout << " --rcuenable[=0] # enable (or disable) input from RCU's" << endl; cout << endl; cout << "rspctl --specinv[=0] [--select=<set>] # enable (or disable) spectral inversion" << endl; + + cout << "rspctl --mode=[0..7] [--select=<set>] # set rcumode in a specific mode" << endl; + cout << " # enable(or disable) input from RCU's" << endl; + cout << " # enable(or disable) spectral inversion" << endl; + cout << " # set the hbadelays to 253" << endl; + cout << endl; cout << "--- Signalprocessing -----------------------------------------------------------------------------------------" << endl; cout << "rspctl --weights [--select=<set>] # get weights as complex values" << endl; @@ -3771,6 +3982,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) { "sdoenable", optional_argument, 0, 'J' }, { "bitmode", optional_argument, 0, 'K' }, { "latency", no_argument, 0, 'L' }, + { "mode", required_argument, 0, 'M' }, { "phase", required_argument, 0, 'P' }, { "tdstatus", no_argument, 0, 'Q' }, { "realdelays", optional_argument, 0, 'R' }, @@ -3788,7 +4000,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) realDelays = false; while (1) { int option_index = 0; - int c = getopt_long(argc, argv, "a::b:c::d:e::g::hi:j::k::l:m:n:o::p::qr::s::t::vw::xy:z::A:BC::D:E::G:H::I::J::K::LP:QR::ST::VXY::Z::1:2:", long_options, &option_index); + int c = getopt_long(argc, argv, "a::b:c::d:e::g::hi:j::k::l:m:n:o::p::qr::s::t::vw::xy:z::A:BC::D:E::G:H::I::J::K::LM:P:QR::ST::VXY::Z::1:2:", long_options, &option_index); if (c == -1) // end of argument list reached? break; @@ -4195,7 +4407,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) bitmodecommand->bitmode(bitmode); } } break; - + case 'j': // --sdo { if (command) @@ -4217,7 +4429,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) } } break; - + case 'k': // --sdomode { if (command) @@ -4459,7 +4671,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) } break; - case 'J': // --sdoenable + case 'J': // --sdoenable { if (command) delete command; @@ -4474,7 +4686,7 @@ Command* RSPCtl::parse_options(int argc, char** argv) } } break; - + case 'Y': // --datastream { if (command) @@ -4566,6 +4778,27 @@ Command* RSPCtl::parse_options(int argc, char** argv) } break; + case 'M': { // mode + if (!optarg) { + logMessage(cerr,"Error: option requires an argument"); + rspctl_exit_code = EXIT_FAILURE; + return 0; + } + int mode = atoi(optarg); + if (mode < 0 || mode > 7) { + logMessage(cerr,"Error: wrong mode must be in range 0..7"); + rspctl_exit_code = EXIT_FAILURE; + return 0; + } + + ModeCommand* modecommand = new ModeCommand(*itsRSPDriver); + command = modecommand; + command->set_ndevices(m_nrcus); + + modecommand->setReceiverMode(mode); + //itsNeedClockOnce = true; + } break; + case '1': // readblock=RSPboard,hexAddress,offset,(datalen|filename) case '2': { // writeblock=RSPboard,hexAddress,offset,(datalen|filename) // allocate the right command diff --git a/MAC/APL/PIC/RSP_Driver/src/rspctl.h b/MAC/APL/PIC/RSP_Driver/src/rspctl.h index 0cf23cb8d3fba1443f63168243c38ed0b5685284..54775cc3c33e22ea57b961d536262efa48d561f3 100644 --- a/MAC/APL/PIC/RSP_Driver/src/rspctl.h +++ b/MAC/APL/PIC/RSP_Driver/src/rspctl.h @@ -270,6 +270,27 @@ private: RCUSettings::Control m_control; }; + +// +// class ModeCommand +// +class ModeCommand : public Command +{ +public: + ModeCommand(GCFPortInterface& port); + virtual ~ModeCommand() {} + virtual void send(); + virtual GCFEvent::TResult ack(GCFEvent& e); + + void setReceiverMode(int mode) { itsMode = mode; } + +private: + int itsMode; + int itsClock; + int itsStage; +}; + + // // class HBACommand // diff --git a/MAC/CMakeLists.txt b/MAC/CMakeLists.txt index 6c1ba3b75ebcbf62766c2ccc1d90f626f85d1d31..f76cd264cd6d6fb6c3a9a02c6c9034edf7f471d0 100644 --- a/MAC/CMakeLists.txt +++ b/MAC/CMakeLists.txt @@ -6,6 +6,7 @@ lofar_add_package(APL) # Application subdirectory lofar_add_package(Deployment) # Deployment related functions lofar_add_package(Navigator2) lofar_add_package(MACTools Tools) +lofar_add_package(MAC_Services Services) lofar_add_package(PVSS_Datapoints Deployment/data/PVSS) lofar_add_package(OTDB_Comps Deployment/data/OTDB) lofar_add_package(StaticMetaData Deployment/data/StaticMetaData) diff --git a/MAC/Deployment/data/OTDB/DPPP.comp b/MAC/Deployment/data/OTDB/DPPP.comp index 482cb96050b2be811035de30e8c18c3f132e9743..e9cb7f00021d7d3ec4cc91f6ab65a0b75fd86c17 100644 --- a/MAC/Deployment/data/OTDB/DPPP.comp +++ b/MAC/Deployment/data/OTDB/DPPP.comp @@ -37,7 +37,7 @@ node msout 4.0.0 development 'node constraint' "Output MeasurementSe #par name I text - 10 0 "-" - "Name of the MeasurementSet" par overwrite I bool - 10 0 F - "When creating a new MS, overwrite if already existing?" par tilenchan I int - 10 0 0 - "For expert user: maximum number of channels per tile in output MS (0 is all channels)" -par tilesize I int - 10 0 1024 - "For expert user: tile size (in Kbytes) for the data columns in the output MS" +par tilesize I int - 10 0 4096 - "For expert user: tile size (in Kbytes) for the data columns in the output MS" par vdsdir I text - 10 0 "A" - "Directory where to put the VDS file; if empty, the MS directory is used." par writefullresflag I bool - 10 0 T - "Write the full resolution flags" diff --git a/MAC/Deployment/data/OTDB/GSM.comp b/MAC/Deployment/data/OTDB/GSM.comp index e4909af200179e3568caa58983016055804a774a..3a8ee36ece12eb557eae31067da0794a504cd247 100644 --- a/MAC/Deployment/data/OTDB/GSM.comp +++ b/MAC/Deployment/data/OTDB/GSM.comp @@ -36,7 +36,7 @@ node GSM 4.0.0 development 'node constraint' "GSM" #-------------------------------------------------------------------------------------------------------- # name dir. type unit prun. vm value constr. descr. #-------------------------------------------------------------------------------------------------------- -par monetdb_hostname I text - 10 100 "lbd002" - 'name of the host where the database is located' +par monetdb_hostname I text - 10 100 "ldb002.offline.lofar" - 'name of the host where the database is located' par monetdb_port I int - 10 100 51000 - 'port number to use for connection to database' par monetdb_name I text - 10 100 "gsm" - 'the name of the database' par monetdb_user I text - 10 100 "gsm" - 'user name used for connecting to database' diff --git a/MAC/Deployment/data/OTDB/createPICfile b/MAC/Deployment/data/OTDB/createPICfile index ce027947bf36c46e0ee6e4fe2cba0349b5a93543..a79fa63ff14470768ae9da75f39d806d66071320 100755 --- a/MAC/Deployment/data/OTDB/createPICfile +++ b/MAC/Deployment/data/OTDB/createPICfile @@ -120,7 +120,7 @@ stations = [] for line in filledLine.findall(open(StationFile).read()): # print "line: =",line if (line.strip() and len(line.split()) == 13): - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, AartFaac ) = line.split() + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPowec, HBAsplit, LBAcal, AartFaac ) = line.split() if (height != "0" and int(stationID) < 900): ringStations.append(ringDict[stnType]+"_"+name) stations.append(name) @@ -199,7 +199,7 @@ for line in software.findall(open(PVSSbasefile).read()): for line in filledLine.findall(open(StationFile).read()): # print "line: =",line if (line.strip() and len(line.split()) == 13): - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, AartFaac ) = line.split() + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPowec, BAsplit, LBAcal, AartFaac ) = line.split() if ( height == "0" or int(stationID) >= 900): continue diff --git a/MAC/Deployment/data/PVSS/License/471_3031_1_Astron_Gen_I_3_314.log b/MAC/Deployment/data/PVSS/License/471_3031_1_Astron_Gen_I_3_314.log new file mode 100644 index 0000000000000000000000000000000000000000..4fee449510623c2eba67ee012d5fe1ab59233476 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/471_3031_1_Astron_Gen_I_3_314.log @@ -0,0 +1,60 @@ + +--------------------------------------------------- +[license] +code = "mcu002.control.lofar 20329200053" +version = 31400002 +sn = "471_3031_1_Astron_Gen_I_3_314/1" +date = 2016.09.15;10:04:35,000 +comment = "CentOS7 MainCU system MCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +ios = 100000 +ssi = 0 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + +--------------------------------------------------- +[license] +code = "mcu199.control.lofar 00033501895" +version = 31400002 +sn = "471_3031_1_Astron_Gen_I_3_314/2" +date = 2016.10.12;15:45:33,000 +comment = "CentOS7 MainCU system MCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +ios = 100000 +ssi = 0 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/471_3031_2_Astron_Gen_II_3_314.log b/MAC/Deployment/data/PVSS/License/471_3031_2_Astron_Gen_II_3_314.log new file mode 100644 index 0000000000000000000000000000000000000000..e887b5fa96288ccb8f48fca55227d404d6041934 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/471_3031_2_Astron_Gen_II_3_314.log @@ -0,0 +1,93 @@ + +--------------------------------------------------- +[license] +code = "ccu002.control.lofar 60371410767" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/1" +date = 2016.09.26;13:29:09,000 +comment = "CentOS7 CepCU system CCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + +--------------------------------------------------- +[license] +code = "CS302N.control.lofar 20356783426" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/2" +date = 2016.09.29;16:32:35,000 +comment = "Core Station CS302N" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + +--------------------------------------------------- +[license] +code = "ccu199.control.lofar 20392303325" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/3" +date = 2016.10.12;15:24:28,000 +comment = "CentOS7 CepCU Test system CCU199" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/Astron_Central_1_shield.txt b/MAC/Deployment/data/PVSS/License/Astron_Central_1_shield.txt index 8beb1c99acbcbbe6e4101b445db3fd5f29ab8535..f928182de56bbc5cc2fac5f1c447a0ab07dc4f26 100644 --- a/MAC/Deployment/data/PVSS/License/Astron_Central_1_shield.txt +++ b/MAC/Deployment/data/PVSS/License/Astron_Central_1_shield.txt @@ -1,29 +1,28 @@ [license] -#hw = 00825320842 -code = "dongleHost 50202878741" -version = 31100002 -sn = "471_3031_1_Astron_Gen_I_2_311" -expire = 0000.00.00;00:00:00,000 -redundancy = 1 -ui = 15 -para = 4 -dde = 5 -event = 1 -api = 80 -excelreport = 5 -http = 15 -infoserver = 5000 -ios = 100000 -comcenter = 5 -maintenance = 0 -scheduler = 0 -ssi = 0 -distributed = 255 -uifix = 0 -parafix = 0 -pararemote = 0 -ctrlext = 1 -update = 0 -licenseMax = 8 -licenseLeft = 8 +code = "dongleHost 40222181284" +version = 31400002 +sn = "471_3031_1_Astron_Gen_I_3_314" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +ios = 100000 +ssi = 0 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 +licenseMax = 8 +licenseLeft = 6 diff --git a/MAC/Deployment/data/PVSS/License/Astron_Station_1_shield.txt b/MAC/Deployment/data/PVSS/License/Astron_Station_1_shield.txt index f69bf55da728991580f8603afe71bf7f50bdbc75..58f1503ff17355e14de3d12c04d4153325ba5eb6 100644 --- a/MAC/Deployment/data/PVSS/License/Astron_Station_1_shield.txt +++ b/MAC/Deployment/data/PVSS/License/Astron_Station_1_shield.txt @@ -1,7 +1,7 @@ [license] -code = "dongleHost 30060581564" -version = 31100002 -sn = "471_3031_2_Astron_Gen_II_2_311" +code = "dongleHost 90090244310" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314" expire = 0000.00.00;00:00:00,000 redundancy = 0 ui = 2 @@ -25,5 +25,5 @@ pararemote = 0 ctrlext = 1 update = 0 licenseMax = 100 -licenseLeft = 83 +licenseLeft = 97 diff --git a/MAC/Deployment/data/PVSS/License/CCU002_option.txt b/MAC/Deployment/data/PVSS/License/CCU002_option.txt new file mode 100644 index 0000000000000000000000000000000000000000..a69ed03666d4d6e6945bafb504b9689afb152c3f --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/CCU002_option.txt @@ -0,0 +1,29 @@ +[license] +code = "ccu002.control.lofar 14006546712" +version = 31400002 +comment = "CentOS7 CepCU system CCU002" +sn = "471_3031_2_Astron_Gen_II_1_38" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +ios = 4000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +ssi = 0 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + diff --git a/MAC/Deployment/data/PVSS/License/CCU199_option.txt b/MAC/Deployment/data/PVSS/License/CCU199_option.txt new file mode 100644 index 0000000000000000000000000000000000000000..8e37f78b5bf8d599798a72433bf4a02c78980ec7 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/CCU199_option.txt @@ -0,0 +1,29 @@ +[license] +code = "ccu199.control.lofar 31504983450" +version = 31400002 +comment = "CentOS7 CepCU Test system CCU199" +sn = "471_3031_2_Astron_Gen_II_1_38" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +ios = 4000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +ssi = 0 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + diff --git a/MAC/Deployment/data/PVSS/License/CS302N_option.txt b/MAC/Deployment/data/PVSS/License/CS302N_option.txt new file mode 100644 index 0000000000000000000000000000000000000000..2cb576524e56437f3ccf203141561ea8382359ba --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/CS302N_option.txt @@ -0,0 +1,29 @@ +[license] +code = "CS302C.control.lofar 32748977553" +version = 31400002 +comment = "Core Station CS302N" +sn = "471_3031_2_Astron_Gen_II_1_38" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +ios = 4000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +ssi = 0 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + + diff --git a/MAC/Deployment/data/PVSS/License/MCU002_option.txt b/MAC/Deployment/data/PVSS/License/MCU002_option.txt new file mode 100644 index 0000000000000000000000000000000000000000..366fd9783759977f8d4de772cd5f608f997b9223 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/MCU002_option.txt @@ -0,0 +1,27 @@ +[license] +code = "mcu002.control.lofar 51476810367" +version = 31400002 +comment = "CentOS7 MainCU system MCU002" +sn = "471_3031_1_Astron_Gen_I_1_314" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +ios = 100000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +ssi = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/MCU199_option.txt b/MAC/Deployment/data/PVSS/License/MCU199_option.txt new file mode 100644 index 0000000000000000000000000000000000000000..f67bb06f77168e557ebe676dd36b390d134543a2 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/MCU199_option.txt @@ -0,0 +1,27 @@ +[license] +code = "mcu199.control.lofar 31976234155" +version = 31400002 +comment = "CentOS7 MainCU system MCU002" +sn = "471_3031_1_Astron_Gen_I_1_314" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +ios = 100000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +ssi = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/Station.rtu b/MAC/Deployment/data/PVSS/License/Station.rtu index a35473f282aaa6d5bbdba075c2b7349c48b4e341..01e8380c97844596318ef3e113f9edee62888929 100644 --- a/MAC/Deployment/data/PVSS/License/Station.rtu +++ b/MAC/Deployment/data/PVSS/License/Station.rtu @@ -5,5 +5,5 @@ Version=1.00 [Contents] ; 1 command for WIBU-BOX with Serial Number 9-5510497: -N5qqe 010NG NNNG9 5NNNe FVB0A AG7yN -LTM15 LG +N5qqe 010NG NNNG9 5NNN0 L4K1A 8L87M +6ZXNA pX diff --git a/MAC/Deployment/data/PVSS/License/shield.CCU002.txt b/MAC/Deployment/data/PVSS/License/shield.CCU002.txt new file mode 100644 index 0000000000000000000000000000000000000000..fd5563bd7f2de1d50abd0b701656b05071e9acff --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/shield.CCU002.txt @@ -0,0 +1,29 @@ +[license] +code = "ccu002.control.lofar 60371410767" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/1" +date = 2016.09.26;13:29:09,000 +comment = "CentOS7 CepCU system CCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/shield.CCU099.txt b/MAC/Deployment/data/PVSS/License/shield.CCU099.txt index 5eef98bf1185686d2e6e705a8b5f645601997742..1ade2af8ce3de2607a30841ba763e0864f00780b 100644 --- a/MAC/Deployment/data/PVSS/License/shield.CCU099.txt +++ b/MAC/Deployment/data/PVSS/License/shield.CCU099.txt @@ -1,5 +1,5 @@ [license] -code = "CCU099 00473561460" +code = "CCU099.control.lofar 00473561460" version = 30800002 sn = "471_3031_2_Astron_Gen_II_1_38/54" date = 2012.02.09;09:59:23,000 diff --git a/MAC/Deployment/data/PVSS/License/shield.CCU199.txt b/MAC/Deployment/data/PVSS/License/shield.CCU199.txt new file mode 100644 index 0000000000000000000000000000000000000000..7548ca1957fa305a74579a705e20c0a5e772cf0a --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/shield.CCU199.txt @@ -0,0 +1,29 @@ +[license] +code = "ccu199.control.lofar 20392303325" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/3" +date = 2016.10.12;15:24:28,000 +comment = "CentOS7 CepCU Test system CCU199" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/shield.CS302N.txt b/MAC/Deployment/data/PVSS/License/shield.CS302N.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8b434ad8d81086ecf5fed6259f0e50805f2e1dd --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/shield.CS302N.txt @@ -0,0 +1,29 @@ +[license] +code = "CS302C.control.lofar 20356783426" +version = 31400002 +sn = "471_3031_2_Astron_Gen_II_3_314/2" +date = 2016.09.29;16:32:35,000 +comment = "Core Station CS302N" +expire = 0000.00.00;00:00:00,000 +redundancy = 0 +ui = 2 +para = 1 +dde = 5 +event = 1 +ios = 4000 +ssi = 0 +api = 80 +excelreport = 5 +http = 0 +infoserver = 1000 +comcenter = 5 +maintenance = 1 +scheduler = 1 +recipe = 1 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/shield.MCU002.txt b/MAC/Deployment/data/PVSS/License/shield.MCU002.txt new file mode 100644 index 0000000000000000000000000000000000000000..932a68b0004b296a39d23a6f6dc0e85af29f9f0d --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/shield.MCU002.txt @@ -0,0 +1,28 @@ +[license] +code = "mcu002.control.lofar 20329200053" +version = 31400002 +sn = "471_3031_1_Astron_Gen_I_3_314/1" +date = 2016.09.15;10:04:35,000 +comment = "CentOS7 MainCU system MCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +ios = 100000 +ssi = 0 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/License/shield.MCU099.txt b/MAC/Deployment/data/PVSS/License/shield.MCU099.txt index abf9a1bb13a8035d92da1ce81f1f6d31e7dea092..d0e1fc094dbc33dc15feddf56e5defeec3590173 100644 --- a/MAC/Deployment/data/PVSS/License/shield.MCU099.txt +++ b/MAC/Deployment/data/PVSS/License/shield.MCU099.txt @@ -1,6 +1,6 @@ [license] -#hw = "MCU099 02772147255" -code = "MCU099 60315490059" +#hw = "MCU099.control.lofar 02772147255" +code = "MCU099.control.lofar 60315490059" version = 30800002 sn = "471_3031_1_Astron_Gen_I_1_38/3" date = 2012.02.09;09:58:38,000 diff --git a/MAC/Deployment/data/PVSS/License/shield.MCU199.txt b/MAC/Deployment/data/PVSS/License/shield.MCU199.txt new file mode 100644 index 0000000000000000000000000000000000000000..db420dbedc3f1ea18e5cdae38658dcc1e02bcee0 --- /dev/null +++ b/MAC/Deployment/data/PVSS/License/shield.MCU199.txt @@ -0,0 +1,28 @@ +[license] +code = "mcu199.control.lofar 00033501895" +version = 31400002 +sn = "471_3031_1_Astron_Gen_I_3_314/2" +date = 2016.10.12;15:45:33,000 +comment = "CentOS7 MainCU system MCU002" +expire = 0000.00.00;00:00:00,000 +redundancy = 1 +ui = 15 +para = 4 +dde = 5 +event = 1 +ios = 100000 +ssi = 0 +api = 80 +excelreport = 5 +http = 15 +infoserver = 5000 +comcenter = 5 +maintenance = 0 +scheduler = 0 +distributed = 255 +uifix = 0 +parafix = 0 +pararemote = 0 +ctrlext = 1 +update = 0 + diff --git a/MAC/Deployment/data/PVSS/bin/create_db_files b/MAC/Deployment/data/PVSS/bin/create_db_files index 00f441c0941e48f441210900f00178bb8930e459..7ae7ae9e62386ea9674bea107752f14c59ee869f 100755 --- a/MAC/Deployment/data/PVSS/bin/create_db_files +++ b/MAC/Deployment/data/PVSS/bin/create_db_files @@ -93,9 +93,9 @@ concatfile() # create_ring_station_list() { - # Only create info for existing stations, which have a height != 0 in StationInfo.dat + # Only create info for existing stations, which have a height != 0 in script.dat # stationname ID stationType ... - cleanlist ${STATIONINFOFILE} | awk '{ + cleanlist ${FILE} | awk '{ if ($3 == "C" && substr($6,1,1) != "0") { print "Core_"$1 } @@ -149,6 +149,22 @@ create_wan_switch_list() rm -f /tmp/wslist } +# +# create_powec_switch_list +# +create_powec_switch_list() +{ + cleanlist ${POWECFILE} | while read powec + do + cleanlist ${powec}POWEC.list | awk -v POWEC=${powec} \ + '{ + print POWEC"_"$1 + }' + done >>/tmp/powlist + concatfile /tmp/powlist + rm -f /tmp/powlist +} + # # labelize somename # @@ -786,6 +802,32 @@ substitute_wan() } ' } +# +# substitute_powec +# +# syntax of the lines must be: dp dpt +# +substitute_powec() +{ + awk -v POWECLIST=${POWECLIST} ' + BEGIN { + nrPow=split(POWECLIST, powname, ":"); + }; + { + hasPow=index($1,"@powec@"); + if (hasPow > 0) { + for (pow in powname) { + dpname=$1; + sub("@powec@", powname[pow], dpname); + print dpname" "$2; + } + } + else { + print $1" "$2; + } + } ' +} + # # substitute_ring_station # @@ -1052,10 +1094,10 @@ create_dpt_file() } # -# add_values inputfile dbtype +# add_dpt_values inputfile dbtype # -add_values() +add_dpt_values() { # setup master point and archives # NOTE: THIS IS ADDED TO THE DataPointTYPES file! @@ -1081,6 +1123,36 @@ add_values() } +# +# add_dp_values inputfile dbtype +# + +add_dp_values() +{ + # setup special points added in dpdef files preceeded by a ? + # NOTE: THIS IS ADDED TO THE DataPoints file! + prevdpt="abc" + cleanlist $1 | sort | while read dpt prefix dbtype leaf dp + do + if [ "${dbtype}" != "$2" ]; then + continue + fi + + if [ "${prevdpt}" == "${dpt}" ]; then + continue + fi + prevdpt=${dpt} + + # Create MasterDataPoint for LOFAR tree elements + + if [ -f ${dpt}.dpdef ]; then + cleancomments ${dpt}.dpdef|grep "^\?" |cut -d! -f2- >>${DESTDIR}/${DP_FILE} + fi + done + echo "added values to ${DESTDIR}/${DP_FILE}" + +} + # # create_dp_file inputfile dbtype # @@ -1104,6 +1176,7 @@ create_dp_file() substitute_observation | \ substitute_wan_switch | \ substitute_wan | \ + substitute_powec | \ substitute_Cabinet_SubRack_RSPBoard_RCU | \ substitute_Cabinet_SubRack_RSPBoard | \ substitute_Cabinet_SubRack_TBBoard | \ @@ -1428,7 +1501,7 @@ if [ "$DBTYPE" == "S" -a "$DBTYPENAME" == "LOCALHOST" ]; then cd $DESTDIR echo "Making files for station $STNNAME in $DESTDIR" elif [ "$DBTYPE" == "M" ]; then - if [ "$HOSTNAME" == "MCU001" -o "$HOSTNAME" == "MCU099" ]; then + if [ "$HOSTNAME" == "MCU001" -o "$HOSTNAME" == "MCU002" -o "$HOSTNAME" == "MCU099" ]; then configdir="/opt/lofar/etc/" dpdefdir="/opt/pvss/dpdef/" DESTDIR=$dpdefdir @@ -1441,7 +1514,7 @@ elif [ "$DBTYPE" == "M" ]; then dpdefdir="../data/" fi elif [ "$DBTYPE" == "C" ]; then - if [ "$HOSTNAME" == "CCU001" -o "$HOSTNAME" == "CCU099" ]; then + if [ "$HOSTNAME" == "CCU001" -o "$HOSTNAME" == "CCU002" -o "$HOSTNAME" == "CCU099" ]; then configdir="/opt/lofar/etc/" dpdefdir="/opt/pvss/dpdef/" DESTDIR=$dpdefdir @@ -1471,6 +1544,7 @@ COMPONENT_FILE=${dpdefdir}/PVSSbase.dpdef CLUSTERFILE=${dpdefdir}/Clusters.list RINGFILE=${dpdefdir}/Rings.list WANFILE=${dpdefdir}/Wan.list +POWECFILE=${dpdefdir}/POWEC.list ERRORFILE=/tmp/Crea.Error INPUTFILE=${dpdefdir}/PVSSDataPoints.base @@ -1486,6 +1560,7 @@ RINGLIST=`concatfile $RINGFILE` RINGSTATIONLIST=`create_ring_station_list` STATIONFIELDLIST=`create_stationfield_list` WANLIST=`concatfile $WANFILE` +POWECLIST = `concatfile $POWECFILE` WANSWITCHLIST=`create_wan_switch_list` # FPGA and URIboard numbers @@ -1509,7 +1584,8 @@ fi # create the desired files create_dpt_file ${INPUTFILE} ${DBTYPE} create_dp_file ${INPUTFILE} ${DBTYPE} -add_values ${INPUTFILE} ${DBTYPE} +add_dpt_values ${INPUTFILE} ${DBTYPE} +add_dp_values ${INPUTFILE} ${DBTYPE} cat ${TMP_FILE} >>${DESTDIR}/${DPT_FILE} rm -f ${TMP_FILE} diff --git a/MAC/Deployment/data/PVSS/data/CEPbase.dpdef b/MAC/Deployment/data/PVSS/data/CEPbase.dpdef index f9b391333f24068a913c3a5dce8a6d8986c983cc..5a9c3051644b98a40acffd2b15ab6dce53a15248 100644 --- a/MAC/Deployment/data/PVSS/data/CEPbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/CEPbase.dpdef @@ -1,19 +1,9 @@ # CEP PVSS Database base types - -# Create missing CtrlDbg internals and DP for remoteStation -# Datapoint/DpId -DpName TypeName -_CtrlDebug_CTRL_5 _CtrlDebug -_CtrlDebug_CTRL_6 _CtrlDebug -_CtrlDebug_CTRL_7 _CtrlDebug -_CtrlDebug_CTRL_8 _CtrlDebug -_CtrlDebug_CTRL_9 _CtrlDebug +# If CEP need some extra DPT DP DPE defines or powerconfig settings +# they can be defined here #Fill some defaults # DpValue ElementName TypeName _original.._value scriptInfo.transferMPs.runDone ScriptInfo 0 -_ValueArchive_2.size.maxDpElGet _ValueArchive 15000 -_ValueArchive_2.size.maxDpElSet _ValueArchive 15000 -_ValueArchive_2.size.maxValuesSet _ValueArchive 1250 -_ValueArchive_2.size.maxValuesGet _ValueArchive 1250 +scriptInfo.setSumAlerts.runDone ScriptInfo 0 diff --git a/MAC/Deployment/data/PVSS/data/MCUbase.dpdef b/MAC/Deployment/data/PVSS/data/MCUbase.dpdef index 882c7937116f1e4d9ac43cf7cbfa7cad8bd0afbc..bc996f787eb3ca9fbe1a81a7aab5eb1c1ea3e70d 100644 --- a/MAC/Deployment/data/PVSS/data/MCUbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/MCUbase.dpdef @@ -138,6 +138,7 @@ NavPanelConfig.NavPanelConfig 1# CobaltOutputProc_Processes 9# FeedbackService_Processes 9# MessageRouter_Processes 9# + PowerUnit_Hardware 9# TypeName NavigatorUserSaves.NavigatorUserSaves 1# @@ -152,6 +153,7 @@ GCFWatchDog.GCFWatchDog 1# online 7# lastUpTime 10# lastDownTime 10# + involved 9# name 9# @@ -173,18 +175,13 @@ __navigator Navigator root NavPanelConfig __gcf_cwd GCFWatchDog rootSaves NavigatorUserSaves -_CtrlDebug_CTRL_5 _CtrlDebug -_CtrlDebug_CTRL_6 _CtrlDebug -_CtrlDebug_CTRL_7 _CtrlDebug -_CtrlDebug_CTRL_8 _CtrlDebug #Fill some defaults # DpValue ElementName TypeName _original.._value scriptInfo.transferMPs.runDone ScriptInfo 0 -_ValueArchive_2.size.maxDpElGet _ValueArchive 1250 -_ValueArchive_2.size.maxDpElSet _ValueArchive 1250 +scriptInfo.setSumAlerts.runDone ScriptInfo 0 __navigator.alarmSettings.emails Navigator "observer@astron.nl" root.LOFAR_Processes NavPanelConfig "Processes/MainCU_Processes.pnl" root.LOFAR_Observations NavPanelConfig "Observations/Observations.pnl" @@ -242,6 +239,7 @@ root.CobaltGPUProc_Processes NavPanelConfig "Processes/ObservationGPUProcEmbedde root.CobaltOutputProc_Processes NavPanelConfig "Processes/ObservationOutputProcs.pnl" root.FeedbackService_Processes NavPanelConfig "Processes/FeedbackService.pnl" root.MessageRouter_Processes NavPanelConfig "Processes/MessageRouter.pnl" +root.PowerUnit_Hardware NavPanelConfig "Hardware/Station_PowerUnits.pnl" rootSaves.Queries.Query NavigatorUserSaves "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 20 AND '_original.._value' < 30", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 30 AND '_original.._value' < 40", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 40 AND '_original.._value' < 50", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 50 AND '_original.._value' < 60" rootSaves.Queries.Short NavigatorUserSaves "All hardware in Maintenance", "All hardware in Test", "All hardware in Suspicious", "All hardware in Alarm" diff --git a/MAC/Deployment/data/PVSS/data/POWEC.list b/MAC/Deployment/data/PVSS/data/POWEC.list new file mode 100644 index 0000000000000000000000000000000000000000..d237fba19c2236699a74e0c06ef715cea9713f98 --- /dev/null +++ b/MAC/Deployment/data/PVSS/data/POWEC.list @@ -0,0 +1,2 @@ +POWEC0 +POWEC1 diff --git a/MAC/Deployment/data/PVSS/data/PVSSDataPoints.base b/MAC/Deployment/data/PVSS/data/PVSSDataPoints.base index 0a77f21d7a859510dfc6e25a598a841731140ced..812ba8dd5cb589b3c5fc41171f46fcf89355cc6a 100644 --- a/MAC/Deployment/data/PVSS/data/PVSSDataPoints.base +++ b/MAC/Deployment/data/PVSS/data/PVSSDataPoints.base @@ -92,6 +92,7 @@ URIBoard URI S Y LOFAR_PIC_@uriboard@ LBAAntenna LBA S Y LOFAR_PIC_@lbaantenna@ HBAAntenna HBA S N LOFAR_PIC_@hbaantenna@ StationInfo STI S Y LOFAR_PIC_StationInfo +PowerUnit POW S Y LOFAR_PIC_@powec@ StnPermSW - S N LOFAR_PermSW # Note: the next 2 lines are neccesary for PVSS2SAS to create the PIC tree. Cluster - S N LOFAR_PermSW_@cluster@ diff --git a/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef b/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef index 25c6a73f1d264ff3c335f93258c1e8e2159f8f36..f2aa9db4e37fa2bc883dbc98d4321c94ed598fd9 100644 --- a/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/PVSSbase.dpdef @@ -82,7 +82,8 @@ ObjectStatus.ObjectStatus 1# childState 21# message 25# leaf 23# - + childSumAlert 25# + TypeName ProcessStatus.ProcessStatus 1# processID 21# @@ -115,20 +116,63 @@ ScriptInfo.ScriptInfo 1# transferMPs 1# debug 23# runDone 23# + setSumAlerts 1# + debug 23# + runDone 23# + # create mp for ProcessStatus and attach an archive +# create SNMP manager, Agent and Pollgroup DpName TypeName _mp_ProcessStatus ProcessStatus _dt_ProcessStatus _DynamicDatapoints +_mp_ObjectStatus ObjectStatus +_dt_ObjectStatus _DynamicDatapoints +_2_SNMPManager _SNMPManager +_SNMP _PollGroup +DummyBit ExampleDP_Bit # create datapoints for ClaimManager,NCFObjectState and lofarSpeedTest +# create missing _CtrlDebug points (needed if more then 4 ctl scripts are running) DpName TypeName ClaimManager ClaimManager +lofarSpeedTest LofarSpeedTest +scriptInfo ScriptInfo +_CtrlDebug_CTRL_5 _CtrlDebug +_CtrlDebug_CTRL_6 _CtrlDebug +_CtrlDebug_CTRL_7 _CtrlDebug +_CtrlDebug_CTRL_8 _CtrlDebug +_CtrlDebug_CTRL_9 _CtrlDebug +_CtrlDebug_CTRL_10 _CtrlDebug +_CtrlDebug_CTRL_11 _CtrlDebug __navObjectState NCFObjectState __resetObjectState NCFObjectState __navObjectStates NCFObjectStates __resetObjectStates NCFObjectStates -lofarSpeedTest LofarSpeedTest -scriptInfo ScriptInfo +#Fill some defaults +# DpValue +ElementName TypeName _original.._value +scriptInfo.transferMPs.runDone ScriptInfo 0 +_ValueArchive_2.size.maxDpElGet _ValueArchive 15000 +_ValueArchive_2.size.maxDpElSet _ValueArchive 15000 +_ValueArchive_2.size.maxValuesSet _ValueArchive 1250 +_ValueArchive_2.size.maxValuesGet _ValueArchive 1250 +_SNMP.Active _PollGroup 1 +_SNMP.PollInterval _PollGroup 5000 +_dt_ObjectStatus.Leaf _DynamicDatapoints "_mp_ObjectStatus.state:_alert_hdl", "_mp_ObjectStatus.childSumAlert:_alert_hdl" +_dt_ObjectStatus.DynamicAttribute _DynamicDatapoints "_da_none", "_da_alert_hdl_sum" + +# AlertValue +ElementName TypeName DetailNr _alert_hdl.._type _alert_hdl.._l_limit _alert_hdl.._u_limit _alert_hdl.._l_incl _alert_hdl.._u_incl _alert_hdl.._panel _alert_hdl.._panel_param _alert_hdl.._help _alert_hdl.._min_prio _alert_hdl.._class _alert_hdl.._text _alert_hdl.._active _alert_hdl.._orig_hdl _alert_hdl.._ok_range _alert_hdl.._hyst_type _alert_hdl.._hyst_time _alert_hdl.._multi_instance _alert_hdl.._l_hyst_limit _alert_hdl.._u_hyst_limit _alert_hdl.._text1 _alert_hdl.._text0 _alert_hdl.._ack_has_prio _alert_hdl.._order _alert_hdl.._dp_pattern _alert_hdl.._dp_list _alert_hdl.._prio_pattern _alert_hdl.._abbr_pattern _alert_hdl.._ack_deletes _alert_hdl.._non_ack _alert_hdl.._came_ack _alert_hdl.._pair_ack _alert_hdl.._both_ack _alert_hdl.._impulse _alert_hdl.._filter_threshold _alert_hdl.._went_text _alert_hdl.._add_text _alert_hdl.._status64_pattern _alert_hdl.._neg _alert_hdl.._status64_match _alert_hdl.._match _alert_hdl.._set +_mp_ObjectStatus.state ObjectStatus 13 "" "" lt:1 LANG:1 "" \0 1 1 0 0 +_mp_ObjectStatus.state ObjectStatus 1 4 -2147483648 0 1 0 lt:1 LANG:1 "" 0 01.01.1970 00:00:00.000 -2147483648 0 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 2 4 0 10 1 0 LOFAR_AlertClass_Off. lt:1 LANG:1 "Off" 0 01.01.1970 00:00:00.000 0 10 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 3 4 10 20 1 0 LOFAR_AlertClass_Operational. lt:1 LANG:1 "Operational" 0 01.01.1970 00:00:00.000 10 20 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 4 4 20 30 1 0 LOFAR_AlertClass_Maintenance. lt:1 LANG:1 "Maintenance" 0 01.01.1970 00:00:00.000 20 30 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 5 4 30 40 1 0 LOFAR_AlertClass_Test. lt:1 LANG:1 "Test" 0 01.01.1970 00:00:00.000 30 40 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 6 4 40 50 1 0 LOFAR_AlertClass_Suspicious. lt:1 LANG:1 "Suspicious" 0 01.01.1970 00:00:00.000 40 50 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 7 4 50 60 1 0 LOFAR_AlertClass_Broken. lt:1 LANG:1 "Broken" 0 01.01.1970 00:00:00.000 50 60 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.state ObjectStatus 8 4 60 2147483647 1 1 LOFAR_AlertClass_Off. lt:1 LANG:1 "DP Offline" 0 01.01.1970 00:00:00.000 60 2147483647 lt:1 LANG:1 "" 0x0 +_mp_ObjectStatus.childSumAlert ObjectStatus 59 "" lt:1 LANG:1 "" 1 lt:1 LANG:1 "" lt:1 LANG:1 "" 1 0 "" DummyBit. "" "" 1 1 1 1 1 0 diff --git a/MAC/Deployment/data/PVSS/data/PowerUnit.dpdef b/MAC/Deployment/data/PVSS/data/PowerUnit.dpdef new file mode 100644 index 0000000000000000000000000000000000000000..c579fb1408133ee3e5269668527d68345d122a89 --- /dev/null +++ b/MAC/Deployment/data/PVSS/data/PowerUnit.dpdef @@ -0,0 +1,129 @@ +# description of the POWEC power unit in the LOFAR stations +nrOfModules int +voltage intArr +current intArr +temperature intArr +OK intArr +nrOfAlarms int +alarmType intArr +alarmReason intArr +alarmTime stringArr +alarmText stringArr +clearAlarmHistory int + +# +# Next points are needed for dataparameterization Do not alter them unless you know what you are doing. tabs are essential in these +# + + +# Datapoint/DpId +?DpName TypeName +?LOFAR_PIC_POWEC0 PowerUnit +?LOFAR_PIC_POWEC1 PowerUnit +?_2_SNMPAgent_1 _SNMPAgent +?_2_SNMPAgent_2 _SNMPAgent + +# Aliases/Comments +?AliasId AliasName CommentName +?_2_SNMPAgent_1. "" lt:1 LANG:1 "POWEC0@@" +?_2_SNMPAgent_2. "" lt:1 LANG:1 "POWEC1@@" + +# DpValue +?ElementName TypeName _original.._value +?LOFAR_PIC_POWEC0.status.leaf PowerUnit 1 +?LOFAR_PIC_POWEC1.status.leaf PowerUnit 1 +?_2_SNMPAgent_1.Access.ReadCommunity _SNMPAgent "public" +?_2_SNMPAgent_1.Access.WriteCommunity _SNMPAgent "public" +?_2_SNMPAgent_1.Access.Timeout _SNMPAgent 100 +?_2_SNMPAgent_1.Access.Retries _SNMPAgent 5 +?_2_SNMPAgent_1.Access.Protocol _SNMPAgent 1 +?_2_SNMPAgent_1.Access.Port _SNMPAgent 161 +?_2_SNMPAgent_1.Status.Timeout _SNMPAgent 1 +?_2_SNMPAgent_1.Redundancy.ReduAgent _SNMPAgent 0 +?_2_SNMPAgent_1.Redundancy.FallBack _SNMPAgent 0 +?_2_SNMPAgent_2.Access.ReadCommunity _SNMPAgent "public" +?_2_SNMPAgent_2.Access.WriteCommunity _SNMPAgent "public" +?_2_SNMPAgent_2.Access.Timeout _SNMPAgent 100 +?_2_SNMPAgent_2.Access.Retries _SNMPAgent 5 +?_2_SNMPAgent_2.Access.Protocol _SNMPAgent 1 +?_2_SNMPAgent_2.Access.Port _SNMPAgent 161 +?_2_SNMPAgent_2.Status.Timeout _SNMPAgent 1 +?_2_SNMPAgent_2.Redundancy.ReduAgent _SNMPAgent 0 +?_2_SNMPAgent_2.Redundancy.FallBack _SNMPAgent 0 + + +?# DistributionInfo +?ElementName TypeName _distrib.._type _distrib.._driver +?LOFAR_PIC_POWEC0.nrOfModules PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.voltage PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.current PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.temperature PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.OK PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.nrOfAlarms PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.clearAlarmHistory PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.alarmType PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.alarmReason PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.alarmTime PowerUnit 56 \2 +?LOFAR_PIC_POWEC0.alarmText PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.nrOfModules PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.voltage PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.current PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.temperature PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.OK PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.nrOfAlarms PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.clearAlarmHistory PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.alarmType PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.alarmReason PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.alarmTime PowerUnit 56 \2 +?LOFAR_PIC_POWEC1.alarmText PowerUnit 56 \2 + +?# DpSmoothMain +?ElementName TypeName _smooth.._type _smooth.._std_type +?LOFAR_PIC_POWEC0.nrOfModules PowerUnit 48 4 +?LOFAR_PIC_POWEC0.voltage PowerUnit 48 4 +?LOFAR_PIC_POWEC0.current PowerUnit 48 4 +?LOFAR_PIC_POWEC0.temperature PowerUnit 48 4 +?LOFAR_PIC_POWEC0.OK PowerUnit 48 4 +?LOFAR_PIC_POWEC0.nrOfAlarms PowerUnit 48 4 +?LOFAR_PIC_POWEC0.clearAlarmHistory PowerUnit 48 4 +?LOFAR_PIC_POWEC0.alarmType PowerUnit 48 4 +?LOFAR_PIC_POWEC0.alarmReason PowerUnit 48 4 +?LOFAR_PIC_POWEC0.alarmTime PowerUnit 48 4 +?LOFAR_PIC_POWEC0.alarmText PowerUnit 48 4 +?LOFAR_PIC_POWEC1.nrOfModules PowerUnit 48 4 +?LOFAR_PIC_POWEC1.voltage PowerUnit 48 4 +?LOFAR_PIC_POWEC1.current PowerUnit 48 4 +?LOFAR_PIC_POWEC1.temperature PowerUnit 48 4 +?LOFAR_PIC_POWEC1.OK PowerUnit 48 4 +?LOFAR_PIC_POWEC1.nrOfAlarms PowerUnit 48 4 +?LOFAR_PIC_POWEC1.clearAlarmHistory PowerUnit 48 4 +?LOFAR_PIC_POWEC1.alarmType PowerUnit 48 4 +?LOFAR_PIC_POWEC1.alarmReason PowerUnit 48 4 +?LOFAR_PIC_POWEC1.alarmTime PowerUnit 48 4 +?LOFAR_PIC_POWEC1.alarmText PowerUnit 48 4 + +?# PeriphAddrMain +?ElementName TypeName _address.._type _address.._reference _address.._poll_group _address.._offset _address.._subindex _address.._direction _address.._internal _address.._lowlevel _address.._active _address.._start _address.._interval _address.._reply _address.._datatype _address.._drv_ident +?LOFAR_PIC_POWEC0.nrOfModules PowerUnit 16 "1_1.3.6.1.4.1.5961.1.3.1.0" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.voltage PowerUnit 16 "1_1.3.6.1.4.1.5961.1.3.2.1.3B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.current PowerUnit 16 "1_1.3.6.1.4.1.5961.1.3.2.1.4B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.temperature PowerUnit 16 "1_1.3.6.1.4.1.5961.1.3.2.1.6B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.nrOfAlarms PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.1.0" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.clearAlarmHistory PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.3.0" "_SNMP" 0 0 \5 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.alarmReason PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.3B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.alarmText PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.5B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 666 "SNMP" +?LOFAR_PIC_POWEC0.alarmType PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.2B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC0.alarmTime PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.4B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 666 "SNMP" +?LOFAR_PIC_POWEC0.OK PowerUnit 16 "1_1.3.6.1.4.1.5961.1.3.2.1.2B" "_SNMP" 0 0 \4 0 0 1 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.nrOfModules PowerUnit 16 "2_1.3.6.1.4.1.5961.1.3.1.0" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.voltage PowerUnit 16 "2_1.3.6.1.4.1.5961.1.3.2.1.3B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.current PowerUnit 16 "2_1.3.6.1.4.1.5961.1.3.2.1.4B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.temperature PowerUnit 16 "2_1.3.6.1.4.1.5961.1.3.2.1.6B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.OK PowerUnit 16 "2_1.3.6.1.4.1.5961.1.3.2.1.2B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.nrOfAlarms PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.1.0" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.clearAlarmHistory PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.3.0" "_SNMP" 0 0 \5 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.alarmReason PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.3B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.alarmText PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.5B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 666 "SNMP" +?LOFAR_PIC_POWEC1.alarmType PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.2B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 661 "SNMP" +?LOFAR_PIC_POWEC1.alarmTime PowerUnit 16 "1_1.3.6.1.4.1.5961.1.12.2.1.4B" "_SNMP" 0 0 \4 0 0 0 02.01.1970 00:00:00.000 01.01.1970 00:00:00.000 01.01.1970 00:00:00.000 666 "SNMP" + diff --git a/MAC/Deployment/data/PVSS/data/StationInfo.dpdef b/MAC/Deployment/data/PVSS/data/StationInfo.dpdef index 0413753487538bf92321927ba4287d29dfdfd669..b46665102734b25cb48954edcf20c71b3c696af9 100644 --- a/MAC/Deployment/data/PVSS/data/StationInfo.dpdef +++ b/MAC/Deployment/data/PVSS/data/StationInfo.dpdef @@ -1,4 +1,5 @@ stationID int +stationIP string N_RSPBoards int N_TBBoards int N_LBAS int @@ -8,6 +9,7 @@ wide_LBAS bool AARTFAAC bool power48On bool power220On bool +nrOfPowerUnits int datastream0 bool datastream1 bool Cabinet.X float diff --git a/MAC/Deployment/data/PVSS/data/Stationbase.dpdef b/MAC/Deployment/data/PVSS/data/Stationbase.dpdef index 5aaa57289ba4aea5092a45c953b3aea92a4cbc58..f757a162534c817f73ee0671494162f02618bf69 100644 --- a/MAC/Deployment/data/PVSS/data/Stationbase.dpdef +++ b/MAC/Deployment/data/PVSS/data/Stationbase.dpdef @@ -125,6 +125,7 @@ NavPanelConfig.NavPanelConfig 1# UniBoard_Hardware 9# LOFAR_Pipelines 9# Observation_Pipelines 9# + PowerUnit_Hardware 9# TypeName NavigatorUserSaves.NavigatorUserSaves 1# @@ -153,26 +154,18 @@ HBAElement.HBAElement 1# -# Create missing CtrlDbg internals and DP for remoteStation +# Create missing CtrlDbg internals # Datapoint/DpId DpName TypeName __navigator Navigator standalone NavPanelConfig rootSaves NavigatorUserSaves -_CtrlDebug_CTRL_5 _CtrlDebug -_CtrlDebug_CTRL_6 _CtrlDebug -_CtrlDebug_CTRL_7 _CtrlDebug -_CtrlDebug_CTRL_8 _CtrlDebug -_CtrlDebug_CTRL_9 _CtrlDebug -_CtrlDebug_CTRL_10 _CtrlDebug -_CtrlDebug_CTRL_11 _CtrlDebug #Fill some defaults # DpValue ElementName TypeName _original.._value scriptInfo.transferMPs.runDone ScriptInfo 0 -_ValueArchive_2.size.maxDpElGet _ValueArchive 1250 -_ValueArchive_2.size.maxDpElSet _ValueArchive 1250 +scriptInfo.setSumAlerts.runDone ScriptInfo 0 __navigator.alarmSettings.emails Navigator "observer@astron.nl" standalone.StnLOFAR_Hardware NavPanelConfig "Hardware/Station.pnl" standalone.StnPIC_Hardware NavPanelConfig "Hardware/Station_Cabinet.pnl" @@ -216,5 +209,6 @@ standalone.UriBoard_Hardware NavPanelConfig "Hardware/Station_UriBoard.pnl" standalone.UniBoard_Hardware NavPanelConfig "Hardware/Station_UniBoard.pnl" standalone.StnLOFAR_Pipelines NavPanelConfig "Observations/Pipelines.pnl" standalone.Observation_Pipelines NavPanelConfig "Observations/Pipeline_overview.pnl" +standalone.PowerUnit_Hardware NavPanelConfig "Hardware/Station_PowerUnits.pnl" rootSaves.Queries.Query NavigatorUserSaves "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 20 AND '_original.._value' < 30", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 30 AND '_original.._value' < 40", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 40 AND '_original.._value' < 50", "SELECT '_original.._value' FROM 'LOFAR_PIC*.status.state' REMOTE ALL WHERE '_original.._value' >= 50 AND '_original.._value' < 60" rootSaves.Queries.Short NavigatorUserSaves "All hardware in Maintenance", "All hardware in Test", "All hardware in Suspicious", "All hardware in Alarm" diff --git a/MAC/Deployment/data/StaticMetaData/RSPImage.conf b/MAC/Deployment/data/StaticMetaData/RSPImage.conf index dca5dd030dabea0d7f88407c19cb12ffd5e7ad94..a11ccc6cbb8fe80d34613903f34c45c150e947a4 100644 --- a/MAC/Deployment/data/StaticMetaData/RSPImage.conf +++ b/MAC/Deployment/data/StaticMetaData/RSPImage.conf @@ -51,7 +51,7 @@ DE603C 4 DE604C 4 DE605C 5 FR606C 4 -SE607C 4 +SE607C 5 UK608C 4 DE609C 4 FI609C 4 diff --git a/MAC/Deployment/data/StaticMetaData/RemoteStation.conf.tmpl b/MAC/Deployment/data/StaticMetaData/RemoteStation.conf.tmpl index ac028c12671b864cc8eb9579551e2eddc5872ca0..5531af9e33e08b4ce6bb2ad00ca666fa7007bd15 100644 --- a/MAC/Deployment/data/StaticMetaData/RemoteStation.conf.tmpl +++ b/MAC/Deployment/data/StaticMetaData/RemoteStation.conf.tmpl @@ -7,6 +7,7 @@ # RS.STATION_ID = @STATION_ID@ +RS.STATION_IP = @STATION_IP@ RS.N_RSPBOARDS = @NR_RSP@ RS.N_TBBOARDS = @NR_TBB@ RS.N_LBAS = @NR_LBA@ @@ -14,4 +15,5 @@ RS.N_HBAS = @NR_HBA@ RS.HBA_SPLIT = @HBA_SPLIT@ RS.WIDE_LBAS = @LBA_WIDE@ RS.AARTFAAC = @AARTFAAC@ +RS.N_POWECS = @NR_POWECS@ diff --git a/MAC/Deployment/data/StaticMetaData/StationInfo.dat b/MAC/Deployment/data/StaticMetaData/StationInfo.dat index e942a82ad2c44c95a6b544b182afded553639b0d..e63b634c81fa905a006325c69f00b016bc380927 100644 --- a/MAC/Deployment/data/StaticMetaData/StationInfo.dat +++ b/MAC/Deployment/data/StaticMetaData/StationInfo.dat @@ -16,51 +16,51 @@ # HBAsplit indicates split HBA field (core stations only) # LBAcal # -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac -#-------------------------------------------------------------------------------------------------------------- +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#--------------------------------------------------------------------------------------------------------------- ## core (1-59) -CS001 1 C 6.8680483 52.9110414 54.43 12 6 96 48 Yes Yes Yes -CS002 2 C 6.8693028 52.9148347 55.90 12 6 96 48 Yes Yes Yes -CS003 3 C 6.8693283 52.9159339 55.19 12 6 96 48 Yes Yes Yes -CS004 4 C 6.8680911 52.9147486 55.94 12 6 96 48 Yes Yes Yes -CS005 5 C 6.8696561 52.9141017 57.58 12 6 96 48 Yes Yes Yes -CS006 6 C 6.87108883 52.9144497 57.78 12 6 96 48 Yes Yes Yes -CS007 7 C 6.8715772 52.9157881 56.56 12 6 96 48 Yes Yes Yes -CS008 8 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS009 9 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS010 10 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS011 11 C 6.8737881 52.9141903 53.68 12 6 96 48 Yes Yes Yes -CS012 12 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS013 13 C 6.8672969 52.9177144 54.98 12 6 96 48 Yes Yes Yes -CS014 14 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS015 15 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS016 16 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS017 17 C 6.8778067 52.9158697 59.23 12 6 96 48 Yes Yes Yes -CS018 18 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS019 19 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS020 20 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS021 21 C 6.8622447 52.9176275 52.73 12 6 96 48 Yes Yes Yes -CS022 22 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS023 23 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS024 24 C 6.8738103 52.9080003 55.80 12 6 96 48 Yes Yes No -CS025 25 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS026 26 C 6.8821933 52.9163728 55.42 12 6 96 48 Yes Yes No -CS027 27 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS028 28 C 6.8755528 52.9254167 52.66 12 6 96 48 Yes Yes No -CS029 29 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS030 30 C 6.8609850 52.9225878 58.01 12 6 96 48 Yes Yes No -CS031 31 C 6.8597219 52.9177706 53.61 12 6 96 48 Yes Yes No -CS032 32 C 6.8603878 52.9121611 59.31 12 6 96 48 Yes Yes Yes +CS001 1 C 6.8680483 52.9110414 54.43 12 6 96 48 1 Yes Yes Yes +CS002 2 C 6.8693028 52.9148347 55.90 12 6 96 48 1 Yes Yes Yes +CS003 3 C 6.8693283 52.9159339 55.19 12 6 96 48 1 Yes Yes Yes +CS004 4 C 6.8680911 52.9147486 55.94 12 6 96 48 1 Yes Yes Yes +CS005 5 C 6.8696561 52.9141017 57.58 12 6 96 48 1 Yes Yes Yes +CS006 6 C 6.87108883 52.9144497 57.78 12 6 96 48 1 Yes Yes Yes +CS007 7 C 6.8715772 52.9157881 56.56 12 6 96 48 1 Yes Yes Yes +CS008 8 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS009 9 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS010 10 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS011 11 C 6.8737881 52.9141903 53.68 12 6 96 48 1 Yes Yes Yes +CS012 12 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS013 13 C 6.8672969 52.9177144 54.98 12 6 96 48 1 Yes Yes Yes +CS014 14 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS015 15 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS016 16 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS017 17 C 6.8778067 52.9158697 59.23 12 6 96 48 1 Yes Yes Yes +CS018 18 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS019 19 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS020 20 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS021 21 C 6.8622447 52.9176275 52.73 12 6 96 48 1 Yes Yes Yes +CS022 22 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS023 23 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS024 24 C 6.8738103 52.9080003 55.80 12 6 96 48 1 Yes Yes No +CS025 25 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS026 26 C 6.8821933 52.9163728 55.42 12 6 96 48 1 Yes Yes No +CS027 27 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS028 28 C 6.8755528 52.9254167 52.66 12 6 96 48 1 Yes Yes No +CS029 29 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS030 30 C 6.8609850 52.9225878 58.01 12 6 96 48 1 Yes Yes No +CS031 31 C 6.8597219 52.9177706 53.61 12 6 96 48 1 Yes Yes No +CS032 32 C 6.8603878 52.9121611 59.31 12 6 96 48 1 Yes Yes Yes # The Remote stations with core station layout (i.e., with HBASplit set to 'yes') -CS101 101 C 6.8805014 52.9223181 57.78 12 6 96 48 Yes Yes No -CS102 102 C 0.0 0.0 0 12 6 96 48 Yes Yes No -CS103 103 C 6.8963725 52.9160403 56.52 12 6 96 48 Yes Yes No -CS201 121 C 6.8829719 52.9128547 56.08 12 6 96 48 Yes Yes No -CS301 141 C 6.8677119 52.9054208 58.75 12 6 96 48 Yes Yes No -CS302 142 C 6.8487681 52.90186 67.32 12 6 96 48 Yes Yes No -CS401 161 C 6.8556800 52.9138667 57.08 12 6 96 48 Yes Yes No -CS501 181 C 6.8665950 52.9263631 56.53 12 6 96 48 Yes Yes No +CS101 101 C 6.8805014 52.9223181 57.78 12 6 96 48 1 Yes Yes No +CS102 102 C 0.0 0.0 0 12 6 96 48 1 Yes Yes No +CS103 103 C 6.8963725 52.9160403 56.52 12 6 96 48 1 Yes Yes No +CS201 121 C 6.8829719 52.9128547 56.08 12 6 96 48 1 Yes Yes No +CS301 141 C 6.8677119 52.9054208 58.75 12 6 96 48 1 Yes Yes No +CS302 142 C 6.8487681 52.90186 67.32 12 6 96 48 1 Yes Yes No +CS401 161 C 6.8556800 52.9138667 57.08 12 6 96 48 1 Yes Yes No +CS501 181 C 6.8665950 52.9263631 56.53 12 6 96 48 1 Yes Yes No ## 33 .. 59 ## @@ -68,7 +68,7 @@ CS501 181 C 6.8665950 52.9263631 56.53 12 6 96 48 Ye ## 60 #MCU001 61 #MCU099 62 -#MCU100 1063 +#MCU100 63 # No numbers above 255 ## 61 .. 69 ## wan (70-79) @@ -80,7 +80,7 @@ CS501 181 C 6.8665950 52.9263631 56.53 12 6 96 48 Ye ## 80 #CCU001 81 #CCU099 82 -#CCU100 1083 +#CCU100 83 # No numbers above 255 ## 82 .. 89 ## spare (90-99) @@ -90,121 +90,121 @@ CS501 181 C 6.8665950 52.9263631 56.53 12 6 96 48 Ye ## arm 1 (100-119) ## 100 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac -#-------------------------------------------------------------------------------------------------------------- -RS104 104 R 6.929932 52.943271 0 12 6 96 48 No Yes No -RS105 105 R 0.0 0.0 0 12 6 96 48 No Yes No -RS106 106 R 6.9854983 52.8744053 29.82 12 6 96 48 No Yes No -RS107 107 R 7.103304 52.928932 0 12 6 96 48 No Yes No -RS108 108 R 0.0 0.0 0 12 6 96 48 No Yes No -RS109 109 R 0.0 0.0 0 12 6 96 48 No Yes No +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#----------------------------------------------------------------------------------------------------------------- +RS104 104 R 6.929932 52.943271 0 12 6 96 48 1 No Yes No +RS105 105 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS106 106 R 6.9854983 52.8744053 29.82 12 6 96 48 1 No Yes No +RS107 107 R 7.103304 52.928932 0 12 6 96 48 1 No Yes No +RS108 108 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS109 109 R 0.0 0.0 0 12 6 96 48 1 No Yes No # 110 .. 119 # # arm 2 (120-139) # 120 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac -#-------------------------------------------------------------------------------------------------------------- -RS202 122 R 0.0 0.0 0 12 6 96 48 No Yes No -RS203 123 R 0.0 0.0 0 12 6 96 48 No Yes No -RS204 124 R 0.0 0.0 0 12 6 96 48 No Yes No -RS205 125 R 6.8965411 52.8571108 59.79 12 6 96 48 No Yes No -RS206 126 R 6.819885 52.808927 0 12 6 96 48 No Yes No -RS207 127 R 6.974889 52.757463 0 12 6 96 48 No Yes No -RS208 128 R 6.9196231 52.6695617 60.66 12 6 96 48 No Yes No -RS209 129 R 0.0 0.0 0 12 6 96 48 No Yes No -RS210 130 R 6.8742114 52.3310447 69.59 12 6 96 48 No Yes No +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#---------------------------------------------------------------------------------------------------------------- +RS202 122 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS203 123 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS204 124 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS205 125 R 6.8965411 52.8571108 59.79 12 6 96 48 1 No Yes No +RS206 126 R 6.819885 52.808927 0 12 6 96 48 1 No Yes No +RS207 127 R 6.974889 52.757463 0 12 6 96 48 1 No Yes No +RS208 128 R 6.9196231 52.6695617 60.66 12 6 96 48 1 No Yes No +RS209 129 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS210 130 R 6.8742114 52.3310447 69.59 12 6 96 48 1 No Yes No ## 130 .. 139 ## ## arm 3 (140-159) ## 140 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac #-------------------------------------------------------------------------------------------------------------- -RS303 143 R 0.0 0.0 0 12 6 96 48 No Yes No -RS304 144 R 0.0 0.0 0 12 6 96 48 No Yes No -RS305 145 R 6.7732369 52.8995903 61.69 12 6 96 48 No Yes No -RS306 146 R 6.7430722 52.8905881 61.76 12 6 96 48 No Yes No -RS307 147 R 6.6816903 52.8033092 65.55 12 6 96 48 No Yes No -RS308 148 R 6.539006 52.82659 0 12 6 96 48 No Yes No -RS309 149 R 6.52464 52.532 0 12 6 96 48 No Yes No -RS310 150 R 6.1386128 52.7648403 40.91 12 6 96 48 No Yes No +RS303 143 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS304 144 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS305 145 R 6.7732369 52.8995903 61.69 12 6 96 48 1 No Yes No +RS306 146 R 6.7430722 52.8905881 61.76 12 6 96 48 1 No Yes No +RS307 147 R 6.6816903 52.8033092 65.55 12 6 96 48 1 No Yes No +RS308 148 R 6.539006 52.82659 0 12 6 96 48 1 No Yes No +RS309 149 R 6.52464 52.532 0 12 6 96 48 1 No Yes No +RS310 150 R 6.1386128 52.7648403 40.91 12 6 96 48 1 No Yes No ## 150 .. 159 ## ## arm 4 (160-179) ## 160 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac #-------------------------------------------------------------------------------------------------------------- -RS402 162 R 0.0 0.0 0 12 6 96 48 No Yes No -RS403 163 R 0.0 0.0 0 12 6 96 48 No Yes No -RS404 164 R 6.808886111 52.93291944 0 12 6 96 48 No Yes No -RS405 165 R 0.0 0.0 0 12 6 96 48 No Yes No -RS406 166 R 6.7505064 53.0183578 65.62 12 6 96 48 No Yes No -RS407 167 R 6.7848725 53.0923619 49.64 12 6 96 48 No Yes No -RS408 168 R 0.0 0.0 0 12 6 96 48 No Yes No -RS409 169 R 6.3574842 52.9804722 59.65 12 6 96 48 No Yes No -RS410 170 R 5.83021 52.99421 0 12 6 96 48 No Yes No -RS411 171 R 6.692146 53.040486 0 12 6 96 48 No Yes No -RS412 172 R 0.0 0.0 0 12 6 96 48 No Yes No -RS413 173 R 6.106016 52.99816 0 12 6 96 48 No Yes No +RS402 162 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS403 163 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS404 164 R 6.808886111 52.93291944 0 12 6 96 48 1 No Yes No +RS405 165 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS406 166 R 6.7505064 53.0183578 65.62 12 6 96 48 1 No Yes No +RS407 167 R 6.7848725 53.0923619 49.64 12 6 96 48 1 No Yes No +RS408 168 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS409 169 R 6.3574842 52.9804722 59.65 12 6 96 48 1 No Yes No +RS410 170 R 5.83021 52.99421 0 12 6 96 48 1 No Yes No +RS411 171 R 6.692146 53.040486 0 12 6 96 48 1 No Yes No +RS412 172 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS413 173 R 6.106016 52.99816 0 12 6 96 48 1 No Yes No ## 170 .. 179 ## ## arm 5 (180-199) ## 180 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal Aartfaac -#-------------------------------------------------------------------------------------------------------------- -RS502 182 R 0.0 0.0 0 12 6 96 48 No Yes No -RS503 183 R 6.8506333 52.9445961 58.65 12 6 96 48 No Yes No -RS504 184 R 0.0 0.0 0 12 6 96 48 No Yes No -RS505 185 R 0.0 0.0 0 12 6 96 48 No Yes No -RS506 186 R 7.03114 53.008596 0 12 6 96 48 No Yes No -RS507 187 R 7.02318 53.0722 0 12 6 96 48 No Yes No -RS508 188 R 6.9536942 53.2403583 45.51 12 6 96 48 No Yes No -RS509 189 R 6.7852089 53.4093164 51.53 12 6 96 48 No Yes No +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#---------------------------------------------------------------------------------------------------------------- +RS502 182 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS503 183 R 6.8506333 52.9445961 58.65 12 6 96 48 1 No Yes No +RS504 184 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS505 185 R 0.0 0.0 0 12 6 96 48 1 No Yes No +RS506 186 R 7.03114 53.008596 0 12 6 96 48 1 No Yes No +RS507 187 R 7.02318 53.0722 0 12 6 96 48 1 No Yes No +RS508 188 R 6.9536942 53.2403583 45.51 12 6 96 48 1 No Yes No +RS509 189 R 6.7852089 53.4093164 51.53 12 6 96 48 1 No Yes No # 190 .. 199 # ## international (200-255) ## 200 -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal -#-------------------------------------------------------------------------------------------------------------- +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#---------------------------------------------------------------------------------------------------------------- # effelsberg -DE601 201 E 6.8835692 50.5229575 396.29 24 12 96 96 No No No +DE601 201 E 6.8835692 50.5229575 396.29 24 12 96 96 1 No No No # garching -DE602 202 E 11.2876647 48.5014758 515.20 24 12 96 96 No No No +DE602 202 E 11.2876647 48.5014758 515.20 24 12 96 96 1 No No No # Tautenburg -DE603 203 E 11.7106958 50.9796781 385.41 24 12 96 96 No No No +DE603 203 E 11.7106958 50.9796781 385.41 24 12 96 96 1 No No No # Potsdam -DE604 204 E 13.0158897 52.4381292 87.02 24 12 96 96 No No No +DE604 204 E 13.0158897 52.4381292 87.02 24 12 96 96 1 No No No # Juelich -DE605 205 E 6.4241444 50.8972719 147.28 24 12 96 96 No No No +DE605 205 E 6.4241444 50.8972719 147.28 24 12 96 96 1 No No No # Nancay -FR606 206 E 2.1926264 47.3759658 192.45 24 12 96 96 No No No +FR606 206 E 2.1926264 47.3759658 192.45 24 12 96 96 1 No No No # Onsala -SE607 207 E 11.9302386 57.3990617 50.34 24 12 96 96 No No No +SE607 207 E 11.9302386 57.3990617 50.34 24 12 96 96 1 No No No # Chillbolton -UK608 208 E -1.4342539 51.1439472 137.66 24 12 96 96 No No No +UK608 208 E -1.4342539 51.1439472 137.66 24 12 96 96 1 No No No # Norderstedt (H=67.246568409726024) # WARNING: JUMP IN STATIONNUMBERS!!!! FI609 HAS NUMBER 209!!!! -DE609 210 E 9.9692322 53.6988092 69.27 24 12 96 96 No No No +DE609 210 E 9.9692322 53.6988092 69.27 24 12 96 96 1 No No No # Borowiec -PL610 211 E 17.0741606 52.2759328 122.29 24 12 96 96 No No No +PL610 211 E 17.0741606 52.2759328 122.29 24 12 96 96 2 No No No # Lazy -PL611 212 E 20.4896131 49.9649386 305.42 24 12 96 48 No No No +PL611 212 E 20.4896131 49.9649386 305.42 24 12 96 48 2 No No No # Baldy -PL612 213 E 20.5897506 53.5939042 178.38 24 12 96 96 No No No +PL612 213 E 20.5897506 53.5939042 178.38 24 12 96 96 2 No No No ## Non-ILT international ## 900 # Finland (to be renamed when it becomes part of ILT) -FI609 901 E 20.7609103 69.0714225 533.25 12 6 48 48 No No No +FI609 901 E 20.7609103 69.0714225 533.25 12 6 48 48 1 No No No ## Test systems -# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA HBAsplit LBAcal -#-------------------------------------------------------------------------------------------------------------- -CS100 230 C 6.3952067 52.8121013 0 6 12 4 4 Yes No No +# name ID ring long lat height nrRSP nrTBB nrLBA nrHBA nrPowec HBAsplit LBAcal Aartfaac +#---------------------------------------------------------------------------------------------------------------- +CS100 230 C 6.3952067 52.8121013 0 6 12 4 4 1 Yes No No #MCU099 231 #CCU099 232 -#RS002 102 R 6.0000000 52.00000000 50.000 4 0 16 16 Yes No +#RS002 102 R 6.0000000 52.00000000 50.000 4 0 16 16 1 Yes No No diff --git a/MAC/Deployment/data/StaticMetaData/Storage+MAC.dat b/MAC/Deployment/data/StaticMetaData/Storage+MAC.dat index f0d0d44e323a94905d9f1a78e6c152302fc17f77..2b1667fd9530afe9276a58518a3447ff21632f3f 100644 --- a/MAC/Deployment/data/StaticMetaData/Storage+MAC.dat +++ b/MAC/Deployment/data/StaticMetaData/Storage+MAC.dat @@ -103,3 +103,53 @@ locus097 00:02:c9:0e:37:fe 10.135.255.22 locus098 00:02:c9:0e:37:fe 10.135.255.23 locus099 00:02:c9:0e:37:fe 10.135.255.24 locus100 00:02:c9:0e:37:fe 10.135.255.25 +cpu01 44:a8:42:32:3d:1e 10.168.153.1 +cpu02 44:a8:42:36:9f:6a 10.168.153.2 +cpu03 44:a8:42:36:61:ea 10.168.153.3 +cpu04 44:a8:42:2f:e7:ae 10.168.153.4 +cpu05 44:a8:42:36:88:3f 10.168.153.5 +cpu06 44:a8:42:38:76:c7 10.168.153.6 +cpu07 44:a8:42:2f:ed:60 10.168.153.7 +cpu08 44:a8:42:2f:6d:d3 10.168.153.8 +cpu09 44:a8:42:2f:d0:58 10.168.153.9 +cpu10 44:a8:42:2f:76:4d 10.168.153.10 +cpu11 44:a8:42:2f:cc:f4 10.168.153.11 +cpu12 44:a8:42:2f:76:71 10.168.153.12 +cpu13 44:a8:42:2f:dc:e4 10.168.153.13 +cpu14 44:a8:42:36:a2:0a 10.168.153.14 +cpu15 44:a8:42:2f:c9:24 10.168.153.15 +cpu16 44:a8:42:2f:77:1d 10.168.153.16 +cpu17 44:a8:42:36:3e:05 10.168.153.17 +cpu18 44:a8:42:2f:97:34 10.168.153.18 +cpu19 44:a8:42:36:65:da 10.168.153.19 +cpu20 44:a8:42:36:67:9b 10.168.153.20 +cpu21 44:a8:42:36:67:ef 10.168.153.21 +cpu22 44:a8:42:36:5a:71 10.168.153.22 +cpu23 44:a8:42:31:df:b3 10.168.153.23 +cpu24 44:a8:42:36:41:35 10.168.153.24 +cpu25 44:a8:42:36:4b:c6 10.168.153.25 +cpu26 44:a8:42:36:9f:22 10.168.153.26 +cpu27 44:a8:42:2f:b8:ce 10.168.153.27 +cpu28 44:a8:42:31:ea:1a 10.168.153.28 +#cpu29 44:a8:00:00:00:00 10.168.153.29 # <--- MAC address TBD +cpu30 44:a8:42:36:55:d3 10.168.153.30 +cpu31 44:a8:42:2f:eb:08 10.168.153.31 +cpu32 44:a8:42:36:ad:90 10.168.153.32 +cpu33 44:a8:42:36:86:b3 10.168.153.33 +cpu34 44:a8:42:36:83:36 10.168.153.34 +cpu35 44:a8:42:31:dc:ce 10.168.153.35 +cpu36 44:a8:42:2f:e7:74 10.168.153.36 +cpu37 44:a8:42:2f:98:b9 10.168.153.37 +cpu38 44:a8:42:36:59:bd 10.168.153.38 +cpu39 44:a8:42:36:42:01 10.168.153.39 +cpu40 44:a8:42:36:3c:25 10.168.153.40 +cpu41 44:a8:42:2f:ed:8e 10.168.153.41 +cpu42 44:a8:42:32:0c:e1 10.168.153.42 +cpu43 44:a8:42:36:a5:0a 10.168.153.43 +cpu44 44:a8:42:36:39:cd 10.168.153.44 +cpu45 44:a8:42:36:83:4e 10.168.153.45 +cpu46 44:a8:42:36:68:2b 10.168.153.46 +cpu47 44:a8:42:36:b0:b4 10.168.153.47 +cpu48 44:a8:42:36:b0:e4 10.168.153.48 +cpu49 44:a8:42:36:84:97 10.168.153.49 +cpu50 44:a8:42:36:a2:16 10.168.153.50 diff --git a/MAC/Deployment/data/StaticMetaData/createFiles b/MAC/Deployment/data/StaticMetaData/createFiles index 447e0c995d1fac07488f445ff925e477e870aebb..b90dd143b4415c19c44f2c520fdad2011e54664b 100755 --- a/MAC/Deployment/data/StaticMetaData/createFiles +++ b/MAC/Deployment/data/StaticMetaData/createFiles @@ -33,7 +33,7 @@ def setWarning(message): # # Find host IP number from DNS # -def findIPNumber(stationName): +def POWEC: """ Returns the IPnumber for a station LCU. In case the station is not known, it will return -1. @@ -279,7 +279,7 @@ def createRSPDriverFile(resultDir, stationName, dataDir,int_local,is_Cobalt): rspDestNode = findRSPDestNodes(stationName, dataDir) print stationName,"matches:",rspDestNode - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPOWECS, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) # Substitute MAC and IP address of destination nodes RSPfile = open(dataDir+"/RSPDriver.conf.tmpl") @@ -476,7 +476,7 @@ def createRSPDriverFile_Test(resultDir, stationName, dataDir, alias): rspDestNode = findRSPDestNodes(stationName, dataDir) #print stationName,"matches:",rspDestNode - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPOWECS, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) # Substitute MAC and IP address of destination nodes RSPfile = open(dataDir+"/RSPDriver.conf.tmpl") @@ -623,7 +623,7 @@ def createTBBDriverFile(resultDir, stationName, dataDir): tbbDestNode = findTBBDestNodes(stationName, dataDir) #print stationName,"matches:",tbbDestNode - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPOWECS, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) #print stationName," has ",nrTBB," TBBoards" # Substitute MAC and IP address of destination nodes @@ -685,14 +685,20 @@ def createRemoteStationFile(resultDir, stationName, dataDir): """ Fills in the markers in the RemoteStation.conf file to match the values for the given station. """ - (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) + (name, stationID, stnType, long, lat, height, nrRSP, nrTBB, nrLBA, nrHBA, nrPOWECS, HBAsplit, LBAcal, Aartfaac ) = findStationInfo(stationName, dataDir) + + # Find Ip number station + ipnumber = findIPNumber(name) + RSconfig = open(dataDir+"/RemoteStation.conf.tmpl").read() RSconfig = RSconfig.replace("@STATION_NAME@", stationName.upper()) RSconfig = RSconfig.replace("@STATION_ID@", stationID) + RSconfig = RSconfig.replace("@STATION_IP@", ipnumber) RSconfig = RSconfig.replace("@NR_RSP@", nrRSP) RSconfig = RSconfig.replace("@NR_TBB@", nrTBB) RSconfig = RSconfig.replace("@NR_LBA@", nrLBA) RSconfig = RSconfig.replace("@NR_HBA@", nrHBA) + RSconfig = RSconfig.replace("@NR_POWECS@", nrPOWECS) RSconfig = RSconfig.replace("@HBA_SPLIT@", HBAsplit) RSconfig = RSconfig.replace("@LBA_WIDE@", LBAcal) RSconfig = RSconfig.replace("@AARTFAAC@", Aartfaac) diff --git a/MAC/GCF/TM/src/CMakeLists.txt b/MAC/GCF/TM/src/CMakeLists.txt index 322a742855d09c7a50bb0880a23220114645bab5..034ac96f131460d69c741a668511cf4607badf44 100644 --- a/MAC/GCF/TM/src/CMakeLists.txt +++ b/MAC/GCF/TM/src/CMakeLists.txt @@ -35,8 +35,7 @@ lofar_add_library(gcftm lofar_add_bin_program(versiongcftm versiongcftm.cc) -install(FILES +lofar_add_sysconf_files( mac.log_prop mac_debug.log_prop - mac_nopvss.log_prop - DESTINATION etc) + mac_nopvss.log_prop) diff --git a/MAC/Navigator2/config/config.navigator.3.10 b/MAC/Navigator2/config/config.navigator.3.10 new file mode 100644 index 0000000000000000000000000000000000000000..5cade6015bd08c95577b3ee11177148ee76b2dd9 --- /dev/null +++ b/MAC/Navigator2/config/config.navigator.3.10 @@ -0,0 +1,29 @@ +[general] +pvss_path = "D:/Siemens/Automation/WinCC_OA/3.10" +proj_path = "D:/Data/TRUNK-MCU001" +proj_path = "D:/Data/TRUNK-Navigator2" +proj_version = "3.10" +langs = "en_US.iso88591" +distributed = 1 +ctrlMaxPendings = 2000 +messageCompression = "zlib-bzip2" +useValueArchive = 1 + + +#local via putty from home or if working with local instantations of mcu/ccc/cs001 +#data = "localhost" +#event = "localhost" + +#mcu001 +#data = "10.149.96.3" +#event = "10.149.96.3" +data = "mcu001.control.lofar" +event = "mcu001.control.lofar" + +#mcu099 +#data = "10.149.96.23" +#event = "10.149.96.23" + +#eventPort = 5997 +#dataPort = 5998 +pmonPort = 2025 diff --git a/MAC/Navigator2/config/config.navigator.3.14 b/MAC/Navigator2/config/config.navigator.3.14 new file mode 100644 index 0000000000000000000000000000000000000000..f7828eebc79c2222ee461bd1515f757feb1b7275 --- /dev/null +++ b/MAC/Navigator2/config/config.navigator.3.14 @@ -0,0 +1,35 @@ +[general] +pvss_path = "D:/Siemens/Automation/WinCC_OA/3.10" +proj_path = "D:/Data/TRUNK-MCU001" +proj_path = "D:/Data/TRUNK-Navigator2" +proj_version = "3.10" +langs = "en_US.iso88591" +distributed = 1 +ctrlMaxPendings = 2000 +# avoid proxyserver to be used for contacts between 3.12 onwards and 3.10 +# can be removed as soon as all installations are updated to 3.14 +mxProxy = "None" + +#Set maximum connect messages size to unlimmited (needed for version 3.12 onwards because of the huge amount of Cobalt processes to connect to +maxConnectMessageSize = 0 +messageCompression = "zlib-bzip2" +useValueArchive = 1 + + +#local via putty from home or if working with local instantations of mcu/ccc/cs001 +#data = "localhost" +#event = "localhost" + +#mcu001 +#data = "10.149.96.3" +#event = "10.149.96.3" +data = "mcu001.control.lofar" +event = "mcu001.control.lofar" + +#mcu099 +#data = "10.149.96.23" +#event = "10.149.96.23" + +#eventPort = 5997 +#dataPort = 5998 +pmonPort = 2025 diff --git a/MAC/Navigator2/config/config.smu002 b/MAC/Navigator2/config/config.smu002 new file mode 100644 index 0000000000000000000000000000000000000000..082ed8db95106b5779fbefbdcce9b8da34d28473 --- /dev/null +++ b/MAC/Navigator2/config/config.smu002 @@ -0,0 +1,12 @@ +[general] +pvss_path = "/opt/WinCC_OA/3.14" +proj_path = "/opt/pvss/pvssproj/Navigator" +proj_version = "3.14" +langs = "en_US.iso88591" +data = "mcu001.control.lofar" +event = "mcu001.control.lofar" +userName = "root" +password = "" +mxproxy = "none" + +pmonPort = 4999 diff --git a/MAC/Navigator2/config/progs.ccu b/MAC/Navigator2/config/progs.ccu index 44065159a51448195e0f40a6f87d2d3da2da48bd..417f70293f2b1156390e107c6aef48ec1cf0dfaf 100644 --- a/MAC/Navigator2/config/progs.ccu +++ b/MAC/Navigator2/config/progs.ccu @@ -5,11 +5,8 @@ auth "" "" PVSS00pmon | manual | 30 | 3 | 1 | PVSS00data | always | 30 | 3 | 1 | PVSS00valarch | always | 30 | 3 | 1 |-num 0 -PVSS00valarch | manual | 30 | 3 | 1 |-num 1 PVSS00valarch | always | 30 | 3 | 1 |-num 2 -PVSS00valarch | manual | 30 | 3 | 1 |-num 3 -PVSS00valarch | manual | 30 | 3 | 1 |-num 4 -PVSS00valarch | manual | 30 | 3 | 1 |-num 5 +PVSS00valarch | always | 30 | 3 | 1 |-num 3 PVSS00event | always | 30 | 3 | 1 | PVSS00ctrl | always | 30 | 3 | 1 |-f pvss_scripts.lst PVSS00sim | always | 30 | 3 | 1 | @@ -19,4 +16,6 @@ PVSS00ctrl | always | 30 | 2 | 2 |monitorStateChanges.c PVSS00ctrl | once | 30 | 2 | 2 |readStationConnections.ctl PVSS00ctrl | always | 30 | 2 | 2 |monitorStateReset.ctl PVSS00ctrl | always | 30 | 2 | 2 |transferMPs.ctl -PVSS00ui | manual | 30 | 2 | 2 |-m para +PVSS00ctrl | always | 30 | 2 | 2 |setSumAlerts.ctl +PVSS00snmp | always | 30 | 2 | 2 |-num 2 +PVSS00ui | manual | 30 | 2 | 2 |-m para -display localhost:10.0 diff --git a/MAC/Navigator2/config/progs.dist.station b/MAC/Navigator2/config/progs.dist.station index 34192b8fc51b393862a635e1d60c7de9e6815873..3035cde2d062a58f1786c473df07af18c8436cdb 100644 --- a/MAC/Navigator2/config/progs.dist.station +++ b/MAC/Navigator2/config/progs.dist.station @@ -16,4 +16,6 @@ PVSS00ctrl | always | 30 | 2 | 2 |monitorStateChanges.c PVSS00ctrl | once | 30 | 2 | 2 |readStationConfigs.ctl PVSS00ctrl | always | 30 | 2 | 2 |monitorStateReset.ctl PVSS00ctrl | always | 30 | 2 | 2 |transferMPs.ctl +PVSS00ctrl | always | 30 | 2 | 2 |setSumAlerts.ctl +PVSS00snmp | always | 30 | 2 | 2 |-num 2 PVSS00ui | manual | 30 | 2 | 2 |-m para -display localhost:10.0 diff --git a/MAC/Navigator2/config/progs.maincu b/MAC/Navigator2/config/progs.maincu index 5e72d998956a1c99e76b21be0d85677079fb481e..0724854b418f518d8b1f53a75507b4a2d4e27cd0 100644 --- a/MAC/Navigator2/config/progs.maincu +++ b/MAC/Navigator2/config/progs.maincu @@ -16,6 +16,7 @@ PVSS00ctrl | always | 30 | 2 | 2 |monitorStateReset.ctl PVSS00ctrl | always | 30 | 2 | 2 |monitorAlarms.ctl PVSS00ctrl | always | 30 | 2 | 2 |gcf_cwd.ctl PVSS00ctrl | always | 30 | 2 | 2 |transferMPs.ctl +PVSS00ctrl | always | 30 | 2 | 2 |setSumAlerts.ctl PVSS00ui | manual | 30 | 2 | 2 |-m para -display localhost:10.0 diff --git a/MAC/Navigator2/config/progs.standalone.station b/MAC/Navigator2/config/progs.standalone.station index c279f585a9898b7acb99a0a936cf4c1314228d2e..73018a603fe08d96912f7b15916d9427f7da8375 100644 --- a/MAC/Navigator2/config/progs.standalone.station +++ b/MAC/Navigator2/config/progs.standalone.station @@ -16,4 +16,6 @@ PVSS00ctrl | once | 30 | 2 | 2 |readStationConfigs.ct PVSS00ctrl | always | 30 | 2 | 2 |monitorStateReset.ctl PVSS00ctrl | always | 30 | 2 | 2 |monitorStationAlarms.ctl PVSS00ctrl | always | 30 | 2 | 2 |transferMPs.ctl +PVSS00ctrl | always | 30 | 2 | 2 |setSumAlerts.ctl +PVSS00snmp | always | 30 | 2 | 2 |-num 2 PVSS00ui | manual | 30 | 2 | 2 |-m para -display localhost:10.0 diff --git a/MAC/Navigator2/panels/Alerts/lofar_alarms.pnl b/MAC/Navigator2/panels/Alerts/lofar_alarms.pnl index d36943ad35cea2d62cf953747564cb6d65fad442..20d2b5d3ecd63ea9ec572b8d790e9032b4c6fe75 100644 --- a/MAC/Navigator2/panels/Alerts/lofar_alarms.pnl +++ b/MAC/Navigator2/panels/Alerts/lofar_alarms.pnl @@ -69,7 +69,7 @@ LANG:1 0 1 LANG:1 33 MS Shell Dlg,-1,11,5,50,0,0,0,0,0 0 -2 -2 1162 832 -E2 "New Alarms" 1 +E3 "New Alarms" 1 LANG:1 10 New Alarms 1 "objects/Alerts/alarms.pnl" 1 LANG:1 0 @@ -83,6 +83,12 @@ LANG:1 0 1 "$choice""Old" +"WinCCOA_Alarms" 1 +LANG:1 15 WinCC-OA Alarms +1 "objects/Alerts/alarmsWinCCOA.pnl" 1 +LANG:1 0 +0 + 0 LAYER, 1 diff --git a/MAC/Navigator2/panels/Hardware/Station_PowerUnits.pnl b/MAC/Navigator2/panels/Hardware/Station_PowerUnits.pnl new file mode 100644 index 0000000000000000000000000000000000000000..08fc96d098fcefd5230e4aa90fbad92dbd0d7cd0 --- /dev/null +++ b/MAC/Navigator2/panels/Hardware/Station_PowerUnits.pnl @@ -0,0 +1,194 @@ +V 11 +1 +LANG:1 17 PowerUnits detail +PANEL,-1 -1 1200 748 N "_3DFace" 0 +"main() +{ + // Initialise the Panel + navPanel_initPanel(\"fw_viewBox\"); + baseDP = g_currentDatapoint; + + reload(); +} + + +void prepareHardwareList() +{ + dynAppend(g_stationList,navFunct_bareDBName(sysName)); + // For this panel PowerUnits should be selectable so we get them for the treelist + + + if (dpGet(sysName+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\",nrOfPowerUnits) == -1) + { + LOG_TRACE(\"Station_PowerUnits.pnl:prepareHardwareList|failed to get \"+sysName+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\"); + return; + } + + + for(int i=0;i<nrOfPowerUnits;i++) + { + dynAppend(g_powerUnitList,i); + } +} + +// +// Callback for dpConnect to action point. +// If there is an action required this point will tell so +// +void doAction(string aDP, string anAction) { + LOG_DEBUG(\"Station_PowerUnits.pnl:doAction| Action required. found: \" + anAction); + // split action into essentials + dyn_string actionString; + if (!navFunct_splitAction(anAction,actionString)) { + return; + } + + LOG_DEBUG(\"Station_PowerUnits.pnl:doAction|found actionString: \" + actionString); + + // Reload + if (actionString[1] == \"Reload\") { + reload(); + } else if (actionString[1] == \"DistChanged\") { + // for dist system bound hardware only, if the distsystem went offline we need to replace + // the screen with a broken connection screen. + if (!g_initializing) { + + // check if this syst is in the connectionlist and down + int iPos=dynContains(g_connections[\"NAME\"],sysName); + if (iPos > 0) { + if (!g_connections[\"UP\"][iPos]) { + navPanel_setEvent(\"invalid DP\",\"ChangePanel\"); + } + } + } + return; + } +} + +void reload() { + + navFunct_clearGlobalLists(); + + // set the hardware selectable items for this screen + prepareHardwareList(); + + if (nrOfPowerUnits == 1) + { + setValue(\"unitsText\",\"text\", sysName + \" has 1 PowerUnit\"); + } + else + { + setValue(\"unitsText\",\"text\", sysName + \" has \" + nrOfPowerUnits + \" PowerUnits\"); + } + + // set panel to ready + g_objectReady=true; + + // trigger that the panel values are calculated and ready + navPanel_setEvent(\"Station_PowerUnits.pnl\",\"Update\"); + +}" 0 + E E E E 1 0 0 0 107 694 +""0 1 +E "#uses \"navPanel.ctl\" + +string baseDP = \"\"; +int nrOfPowerUnits = 0; +" 0 + 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 6 Layer1 +1 16 1 "" 924 +0 +1 17 1 "" 916 +0 +1 18 1 "1" 1 +0 +1 19 1 "" 917 +0 +1 20 1 "" 918 +0 +1 21 1 "" 920 +0 +1 22 1 "" 921 +0 +1 23 1 "" 922 +0 +1 24 2 "" 924 +0 +1 25 2 "" 916 +0 +1 26 2 "1" 1 +0 +1 27 2 "" 917 +0 +1 28 2 "" 918 +0 +1 29 2 "" 920 +0 +1 30 2 "" 921 +0 +1 31 2 "" 922 +0 +2 15 +"unitsText" +"" +1 429.9770114942529 40 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +129 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E 0.9885057471264368 0 1 4.919540229885051 0 0 E 430 40 579 56 +0 2 2 "0s" 0 0 0 192 0 0 430 40 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 21 CS001 has 1 PowerUnit +0 +LAYER, 1 +1 +LANG:1 6 Layer2 +0 +LAYER, 2 +1 +LANG:1 6 Layer3 +0 +LAYER, 3 +1 +LANG:1 6 Layer4 +0 +LAYER, 4 +1 +LANG:1 6 Layer5 +0 +LAYER, 5 +1 +LANG:1 6 Layer6 +0 +LAYER, 6 +1 +LANG:1 6 Layer7 +0 +LAYER, 7 +1 +LANG:1 6 Layer8 +0 +3 1 "powerunit0" -1 +"objects\\Hardware\\Station_PowerUnit.pnl" 7 213 T 128 U +1 +"$unitNumber""0" +3 2 "powerunit1" -1 +"objects\\Hardware\\Station_PowerUnit.pnl" 7 513 T 129 U +1 +"$unitNumber""1" +0 diff --git a/MAC/Navigator2/panels/Test/Claim_Viewer.pnl b/MAC/Navigator2/panels/Test/Claim_Viewer.pnl new file mode 100644 index 0000000000000000000000000000000000000000..ee0db472fd4d0fad31159137ef0f58f6a8f30f2f --- /dev/null +++ b/MAC/Navigator2/panels/Test/Claim_Viewer.pnl @@ -0,0 +1,708 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 1200 814 N "_3DFace" 0 +"main() +{ + // check if the required datapoint for this view are enabled and accessible + string baseDP = MainDBName+\"ClaimManager.cache\"; + if (dpExists(baseDP)) { + if (dpConnect(\"updateCacheClaims\",true, baseDP + \".newObjectNames:_online.._value\", + baseDP + \".DPNames:_online.._value\", + baseDP + \".claimDates:_online.._value\", + baseDP + \".freeDates:_online.._value\", + baseDP + \".newObjectNames:_online.._invalid\") == -1) + { + LOG_DEBUG(\"Claim_Viewer.pnl:main|Couldn't connect to: \"+baseDP); + } + else + { + LOG_DEBUG(\"Claim_Viewer.pnl:main|Connected to: \" + baseDP); + } + } + else + { + if (!isStandalone()) DebugN(\"Claim_Viewer.pnl:main|Couldn't find DP to connect to: \"+baseDP); + } +}" 0 + E E E E 1 -1 -1 0 0 0 +""0 1 +E "#uses \"navigator.ctl\" +#uses \"claimManager.ctl\" + +updateCacheClaims(string dp1, dyn_string objectNames, + string dp2, dyn_string DPNames, + string dp3, dyn_time claimDates, + string dp4, dyn_time freeDates, + string dp5, bool invalid) +{ + cacheTable.deleteAllLines(); + newClaim.foreCol(\"Lofar_broken\"); + if (invalid) + { + LOG_WARN(\"Claim_Viewer.pnl:updateCacheClaims|ClaimManager.cache is invalid\"); + cacheTable.backCol(\"Lofar_invalid\"); + newClaim.foreCol(\"Lofar_operational\"); + return; + } + + int unused = 0; + int claimed = 0; + int freed = 0; + int memInUse = 0; + bool full = false; +// dyn_string claimedMS; + + for (int i=1; i<= dynlen(objectNames);i++) + { + time claim = claimDates[i]; + time free = freeDates[i]; + cacheTable.appendLine(\"temp\", DPNames[i], \"name\", objectNames[i], \"claim\", claim, \"free\", free); + if (period(claim) == 0 && period(free) == 0) + { + unused += 1; + } + else if (period(claim) == 0) + { + freed += 1; + } + else if (period(free) == 0) + { + claimed += 1; + } + else + { + full = true; + } + } + claimTableUnused.text = unused; + claimTableFree.text = freed; + claimTableClaimed.text = claimed; + if (full) + { + claimTableFull.text = \"full\"; + claimTableFullBorder.backCol(\"Lofar_broken\"); + } + else + { + claimTableFull.text = \"free space\"; + claimTableFullBorder.backCol(\"Lofar_operational\"); + } + newClaim.foreCol(\"Lofar_operational\"); + + setValue(\"memInUse\",\"text\",g_usedClaims); + setValue(\"memFree\",\"text\",g_freeClaims); +}" 0 + 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +6 22 +"claimTableFullBorder" +"" +1 610 130 E E E 1 E 1 E N {0,0,0} E N "Lofar_operational" E E + E E +22 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E 1.8 0 1 -516 70 1 E 610 130 650 150 +30 0 +"FRAME2" +"" +1 8 6 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E + E E +0 0 0 0 0 0 +E E E +1 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 0 1 0 1 E 0.825 0 0.88888888888889 3.4 4.666666666666661 0 E 8 6 409 97 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 1 +LANG:1 5 Claim +30 1 +"FRAME3" +"" +1 8 6 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E + E E +1 0 0 0 0 0 +E E E +1 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 0 1 0 1 E 1.1 0 0.6666666666666674 1.200000000000069 86.00000000000003 0 E 8 6 409 97 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 1 +LANG:1 8 Response +2 2 +"PRIMITIVE_TEXT3" +"" +1 -3478.000000000001 -3136 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E + E E +2 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 2 1 1 2 1 E 0.9999999999999999 0 1 -700 -650 1 E 722 764 802 788 +0 2 2 "0s" 0 0 0 64 0 0 722 764 1 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 1 +LANG:1 3 DP: +13 3 +"PUSH_BUTTON16" +"" +1 20 60 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +3 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 18 58 102 88 + +T +1 +LANG:1 5 claim +"main() +{ + dpSet( + \"ClaimManager.request.typeName\" , \"Observation\", + \"ClaimManager.request.newObjectName\" , NAME.text ); + + dyn_string dpNamesWait = makeDynString(\"ClaimManager.response.newObjectName:_original.._value\"); + dyn_string dpNamesReturn = makeDynString(\"ClaimManager.response.typeName:_original.._value\", + \"ClaimManager.response.newObjectName:_original.._value\", + \"ClaimManager.response.DPName:_original.._value\", + \"ClaimManager.response.claimDate:_original.._value\"); + dyn_anytype conditions=NAME.text; + dyn_anytype returnValues; + + int status = dpWaitForValue( dpNamesWait, conditions, dpNamesReturn, returnValues, 25 ); + + if ( status == -1 ) { + DebugN( \"Event_Viewer.pnl:Error in dpWaitFor Value\" ); + + DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); + + DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); + + DebugN( \"Event_Viewer.pnl:dpNamesReturn : \" + dpNamesReturn ); + + DebugN( \"returnValues : \" + returnValues ); + } else if ( dynlen(getLastError()) != 0 ) { + + DebugN( \"Event_Viewer.pnl:Error returned in message dpWaitForValue\" ); + + // Reaction: e.g. output + + DebugN( getLastError() ); + + DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); + + DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); + + DebugN( \"dpNamesReturn : \" + dpNamesReturn ); + + DebugN( \"Event_Viewer.pnl:returnValues : \" + returnValues ); + } else { + DebugN( \"Event_Viewer.pnl:dpWaitForValue : everything ok\" ); + + DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); + + DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); + + DebugN( \"Event_Viewer.pnl:dpNamesReturn : \" + dpNamesReturn ); + + DebugN( \"Event_Viewer.pnl:returnValues : \" + returnValues ); + + DebugN(\"Set txt_response to: \"+ returnValues[3]); + setValue(\"txt_response\",\"text\",returnValues[3]); + + } +}" 0 + E E E +14 4 +"NAME" +"" +1 120 30 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +4 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 118 28 332 56 +3 "0s" 0 0 0 0 0 -1 E E E +13 5 +"PUSH_BUTTON17" +"" +1 350 20 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +5 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 348 18 454 92 + +T +1 +LANG:1 29 Display +claims +( LogViewer ) +"main() +{ + // global dyn_string strClaimDPName; // datapoint that was claimed + // global dyn_string strClaimObjectName; // Actual object name + + DebugN( \"*********************************************\" ); + DebugN( \"Our global variable 'strClaimDPName' and 'strClaimObjectName' hold following records\" ); + + if( dynlen( strClaimObjectName )) + for( int t = 1; t <= dynlen( strClaimDPName ); t++) + { + DebugN( strClaimDPName[t] + \",\" + strClaimObjectName[t] ); + } + +}" 0 + E E E +14 6 +"txt_response" +"" +1 69.99999999999977 114 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +6 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 68 112 442 140 +3 "0s" 0 0 0 0 0 -1 E E E +13 7 +"PUSH_BUTTON21" +"" +1 130 60 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +7 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 128 58 212 88 + +T +1 +LANG:1 4 free +"main() +{ + int ret = dpSet( + \"ClaimManager.reset.typeName\" , \"Observation\", + \"ClaimManager.reset.objectName\" , NAME.text ); + + if (ret < 0) DebugN(\"something went wrong resetting DP \"+NAME.txt+ \" \" + getLastError()); + + +}" 0 + E E E +2 8 +"PRIMITIVE_TEXT2" +"" +1 32 32 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E + E E +8 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 2 1 1 2 1 E U 1 E 32 32 112 56 +0 2 2 "0s" 0 0 0 64 0 0 32 32 1 +1 +LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 +0 1 +LANG:1 16 New object name: +25 9 +"cacheTable" +"" +1 -1.261213355974178e-013 249.9999999999999 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +9 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 30 Sans Serif,9,-1,5,50,0,0,0,0,0 +0 -2 248 1172 802 +EE 1 0 1 4 0 "temp" 43 1 0 "s" 1 +LANG:1 4 temp +E +1 +LANG:1 0 + +400 "name" 43 1 0 "s" 1 +LANG:1 4 name +E +1 +LANG:1 0 + +400 "claim" 18 1 0 "s" 1 +LANG:1 5 claim +E +1 +LANG:1 0 + +175 "free" 18 1 0 "s" 1 +LANG:1 4 free +E +1 +LANG:1 0 + +175 +14 14 10 10 +1 +LANG:1 30 Sans Serif,9,-1,5,50,0,0,0,0,0 +0 0 1 1 1 7 +1 0 +2 12 +"PRIMITIVE_TEXT4" +"" +1 602 92 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +12 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 602 92 691 111 +0 2 2 "0s" 0 0 0 192 0 0 602 92 1 +1 +LANG:1 35 MS Shell Dlg 2,12,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 10 claimTable +2 13 +"PRIMITIVE_TEXT5" +"" +1 900 90 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +13 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 900 90 965 109 +0 2 2 "0s" 0 0 0 192 0 0 900 90 1 +1 +LANG:1 35 MS Shell Dlg 2,12,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 6 Memory +2 14 +"PRIMITIVE_TEXT6" +"" +1 522 122 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +14 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 522 122 569 138 +0 2 2 "0s" 0 0 0 192 0 0 522 122 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 6 unused +2 15 +"PRIMITIVE_TEXT7" +"" +1 522 147 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +15 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 522 147 570 163 +0 2 2 "0s" 0 0 0 192 0 0 522 147 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 7 claimed +2 16 +"PRIMITIVE_TEXT8" +"" +1 522 172 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +16 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 522 172 549 188 +0 2 2 "0s" 0 0 0 192 0 0 522 172 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 4 free +2 18 +"claimTableUnused" +"" +1 622 122 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +18 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 622 122 643 138 +0 2 2 "0s" 0 0 0 192 0 0 622 122 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,50,0,0,0,0,0 +0 1 +LANG:1 3 250 +2 19 +"claimTableClaimed" +"" +1 622 147 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +19 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 622 147 643 163 +0 2 2 "0s" 0 0 0 192 0 0 622 147 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,50,0,0,0,0,0 +0 1 +LANG:1 3 250 +2 20 +"claimTableFree" +"" +1 622 172 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +20 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 622 172 643 188 +0 2 2 "0s" 0 0 0 192 0 0 622 172 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,50,0,0,0,0,0 +0 1 +LANG:1 3 250 +2 21 +"claimTableFull" +"" +1 584 202 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +21 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 584 202 653 218 +0 2 2 "0s" 0 0 0 192 0 0 584 202 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 10 free space +2 23 +"newClaim" +"" +1 610 70 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +23 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 610 70 674 86 +0 2 2 "0s" 0 0 0 192 0 0 610 70 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 9 new Claim +2 24 +"PRIMITIVE_TEXT15" +"" +1 812 147 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +24 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 812 147 868 163 +0 2 2 "0s" 0 0 0 192 0 0 812 147 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 9 Nr in use +2 25 +"memInUse" +"" +1 930 147 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +25 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 930 147 951 163 +0 2 2 "0s" 0 0 0 192 0 0 930 147 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,50,0,0,0,0,0 +0 1 +LANG:1 3 250 +2 26 +"PRIMITIVE_TEXT16" +"" +1 812 172 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +26 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 812 172 857 188 +0 2 2 "0s" 0 0 0 192 0 0 812 172 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 7 Nr free +2 27 +"memFree" +"" +1 930 172 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +27 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 930 172 951 188 +0 2 2 "0s" 0 0 0 192 0 0 930 172 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,50,0,0,0,0,0 +0 1 +LANG:1 3 250 +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/CobaltTestStub.pnl b/MAC/Navigator2/panels/Test/CobaltTestStub.pnl new file mode 100644 index 0000000000000000000000000000000000000000..315ecb15490295298f0a1705ea268b849442921b --- /dev/null +++ b/MAC/Navigator2/panels/Test/CobaltTestStub.pnl @@ -0,0 +1,75 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 500 400 N "_3DFace" 0 +E E E E E 1 -1 -1 0 100 140 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +13 0 +"PUSH_BUTTON1" +"" +1 100 140 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 98 138 222 164 + +T +1 +LANG:1 14 write gpuprocs +"main() +{ + string basedp = \"CCU001:LOFAR_ObsSW_TempObs0127_OSCBT00\"; + string dp; + for (int cbt = 1 ; cbt< 8 ; cbt++) { + for (int gpu = 0 ; gpu< 2 ; gpu++) { + dp = basedp+cbt+\"_CobaltGPUProc0\"+gpu; + dpSet(dp+\".dataProductType\",\"Correlated\"); + } + } +}" 0 + E E E +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/Event_Viewer.pnl b/MAC/Navigator2/panels/Test/Event_Viewer.pnl index d1f0cd1235a00014a12482e1e49516de226b87d8..b278abc59f4ecf6868cbd3c6135bc37fbe2d3833 100644 --- a/MAC/Navigator2/panels/Test/Event_Viewer.pnl +++ b/MAC/Navigator2/panels/Test/Event_Viewer.pnl @@ -1,7 +1,7 @@ V 11 1 LANG:1 12 Event Viewer -PANEL,-1 -1 1200 843 N "_3DFace" 0 +PANEL,-1 -1 1200 813 N "_3DFace" 0 "main() { int retry=0; @@ -73,6 +73,24 @@ DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 LAYER, 0 1 LANG:1 0 +30 2 +"FRAME1" +"" +1 10 1 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E + E E +2 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 2 1 0 1 E 0.986193293885602 0 0.450511945392491 400.138067061144 249.5494880546075 0 E 10 1 518 295 +1 +LANG:1 32 Arial Black,-1,13,5,75,0,0,0,0,0 +0 1 +LANG:1 19 Navigator Framework 13 320 "PUSH_BUTTON10" "" @@ -100,28 +118,10 @@ LANG:1 14 show g_TBBList } }" 0 E E E -30 2 -"FRAME1" -"" -1 10 1 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E - E E -2 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -1 -"dashclr"N "_Transparent" -E E 0 2 1 0 1 E 0.986193293885602 0 0.450511945392491 395.138067061144 298.549488054608 0 E 10 1 518 295 -1 -LANG:1 32 Arial Black,-1,13,5,75,0,0,0,0,0 -0 1 -LANG:1 19 Navigator Framework 14 3 "text_event" "" -1 557 348 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 560 297.0152671755725 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 3 0 0 0 0 0 E E E @@ -132,12 +132,12 @@ LANG:1 0 0 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 -0 555 346 899 369 +0 558 295 902 318 3 "0s" 0 0 0 0 0 -1 E E E 2 6 "PRIMITIVE_TEXT4" "" -1 1236 409.3240071683281 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E +1 1375 364.6318240169413 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E E E 6 0 0 0 0 0 E E E @@ -147,7 +147,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E 1 0 1.00763358778626 136 3.16793893129752 1 E 283 371 384 386 +E E 0 1 1 2 1 E 1 0 1.00763358778626 139 -47.81679389312999 1 E 283 371 384 386 0 2 2 "0s" 0 0 0 192 0 0 283 371 1 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 @@ -156,7 +156,7 @@ LANG:1 18 Initiating Object: 2 7 "PRIMITIVE_TEXT5" "" -1 1241 396.4233462865291 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E +1 1381 354.8693260291744 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E E E 7 0 0 0 0 0 E E E @@ -166,7 +166,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E 1 0 1.00763358778626 137 6.40458015267157 1 E 282 340 379 355 +E E 0 1 1 2 1 E 1 0 1.00763358778626 140 -44.58015267175594 1 E 282 340 379 355 0 2 2 "0s" 0 0 0 192 0 0 282 340 1 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 @@ -175,7 +175,7 @@ LANG:1 15 Event Received: 2 8 "PRIMITIVE_TEXT6" "" -1 1241 426.3243640761156 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E +1 1381 379.5405805957806 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E E E 8 0 0 0 0 0 E E E @@ -185,7 +185,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E 1 0 1.00763358778626 137 0.946564885495969 1 E 282 400 342 415 +E E 0 1 1 2 1 E 1 0 1.00763358778626 140 -50.03816793893154 1 E 282 400 342 415 0 2 2 "0s" 0 0 0 192 0 0 282 400 1 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 @@ -194,7 +194,7 @@ LANG:1 10 Selection: 14 9 "text_initiator" "" -1 557 376 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 560 325.0152671755725 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 9 0 0 0 0 0 E E E @@ -205,12 +205,12 @@ LANG:1 0 0 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 -0 555 374 899 397 +0 558 323 902 346 3 "0s" 0 0 0 0 0 -1 E E E 14 11 "text_selection" "" -1 557 403 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 563 349.0152671755725 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 11 0 0 0 0 0 E E E @@ -221,7 +221,7 @@ LANG:1 0 0 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 -0 555 401 899 424 +0 561 347 905 370 3 "0s" 0 0 0 0 0 -1 E E E 4 18 "LINE4" @@ -278,7 +278,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 523 90 523 306 +E E 0 3 1 2 1 E 523 90 523 260 4 24 "LINE8" "" @@ -310,7 +310,7 @@ E E 0 3 1 2 1 E 526 91 534 102 4 28 "LINE10" "" -1 697 307 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 710 280 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 32 0 0 0 0 0 E E E @@ -320,7 +320,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 697 307 751 186 +E E 0 3 1 2 1 E 710 280 751 186 4 29 "LINE11" "" @@ -352,7 +352,7 @@ E E 0 3 1 2 1 E 752 188 756 199 4 33 "LINE13" "" -1 678 433 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 700 380 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 39 0 0 0 0 0 E E E @@ -362,11 +362,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 678 433 745 541 +E E 0 3 1 2 1 E 700 380 767 488 4 34 "LINE14" "" -1 745 539 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 768 492 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 40 0 0 0 0 0 E E E @@ -376,11 +376,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 745 539 749 527 +E E 0 3 1 2 1 E 768 492 772 480 4 35 "LINE15" "" -1 741 540 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 764 493 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 41 0 0 0 0 0 E E E @@ -390,11 +390,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 741 540 727 533 +E E 0 3 1 2 1 E 764 493 750 486 4 40 "LINE16" "" -1 530 428.9999999999999 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 530 389.9999999999998 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 50 0 0 0 0 0 E E E @@ -404,7 +404,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 530 429 530 740 +E E 0 3 1 2 1 E 530 390 530 740 4 41 "LINE17" "" @@ -436,7 +436,7 @@ E E 0 3 1 2 1 E 520 726 529 736 4 43 "LINE19" "" -1 473 430 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E +1 470 390 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E E E 53 0 0 0 0 0 E E E @@ -446,7 +446,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 3 1 2 1 E 473 430 260 648 +E E 0 3 1 2 1 E 470 390 260 648 4 44 "LINE20" "" @@ -604,7 +604,7 @@ E E 0 3 1 2 1 E 184 290 196 290 14 134 "txt_datapoint" "" -1 557 321.694656488549 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 560 270.7099236641215 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 123 0 0 0 0 0 E E E @@ -615,12 +615,12 @@ LANG:1 0 0 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 -0 555 320 899 343 +0 558 269 902 292 3 "0s" 0 0 0 0 0 -1 E E E 2 135 "PRIMITIVE_TEXT11" "" -1 1466.082645446156 237.4157717744857 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E +1 1686.14777928896 166.6326860628405 E E E 1 E 1 E N "_WindowText" E N "_3DFace" E E E E 125 0 0 0 0 0 E E E @@ -630,7 +630,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E 1.06930693069307 0 1.00763358778626 117.594059405941 -19.5954198473284 1 E 282 340 391 355 +E E 0 1 1 2 1 E 1.06930693069307 0 1.00763358778626 118.4554455445543 -72.59541984732846 1 E 282 340 391 355 0 2 2 "0s" 0 0 0 192 0 0 282 340 1 1 LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 @@ -814,7 +814,7 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 1 3 1 2 1 E 940 10 940 640 +E E 1 3 1 2 1 E 940 10 940 570 13 316 "PUSH_BUTTON6" "" @@ -1021,207 +1021,6 @@ E E 2 1 1 2 1 E U 1 E 720 680 800 704 LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 0 1 LANG:1 16 New object name: -13 496 -"PUSH_BUTTON16" -"" -1 720 710 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E - E E -170 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -0 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 718 708 802 738 - -T -1 -LANG:1 5 claim -"main() -{ - dpSet( - \"ClaimManager.request.typeName\" , \"Observation\", - \"ClaimManager.request.newObjectName\" , NAME.text ); - - dyn_string dpNamesWait = makeDynString(\"ClaimManager.response.newObjectName:_original.._value\"); - dyn_string dpNamesReturn = makeDynString(\"ClaimManager.response.typeName:_original.._value\", - \"ClaimManager.response.newObjectName:_original.._value\", - \"ClaimManager.response.DPName:_original.._value\", - \"ClaimManager.response.claimDate:_original.._value\"); - dyn_anytype conditions=NAME.text; - dyn_anytype returnValues; - - int status = dpWaitForValue( dpNamesWait, conditions, dpNamesReturn, returnValues, 25 ); - - if ( status == -1 ) { - DebugN( \"Event_Viewer.pnl:Error in dpWaitFor Value\" ); - - DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); - - DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); - - DebugN( \"Event_Viewer.pnl:dpNamesReturn : \" + dpNamesReturn ); - - DebugN( \"returnValues : \" + returnValues ); - } else if ( dynlen(getLastError()) != 0 ) { - - DebugN( \"Event_Viewer.pnl:Error returned in message dpWaitForValue\" ); - - // Reaction: e.g. output - - DebugN( getLastError() ); - - DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); - - DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); - - DebugN( \"dpNamesReturn : \" + dpNamesReturn ); - - DebugN( \"Event_Viewer.pnl:returnValues : \" + returnValues ); - } else { - DebugN( \"Event_Viewer.pnl:dpWaitForValue : everything ok\" ); - - DebugN( \"Event_Viewer.pnl:dpNamesWait : \" + dpNamesWait ); - - DebugN( \"Event_Viewer.pnl:conditions : \" + conditions ); - - DebugN( \"Event_Viewer.pnl:dpNamesReturn : \" + dpNamesReturn ); - - DebugN( \"Event_Viewer.pnl:returnValues : \" + returnValues ); - - DebugN(\"Set txt_response to: \"+ returnValues[3]); - setValue(\"txt_response\",\"text\",returnValues[3]); - - } -}" 0 - E E E -14 498 -"NAME" -"" -1 820 680 E E E 1 E 1 E N "_WindowText" E N "_Window" E E - E E -174 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -0 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 818 678 1032 706 -3 "0s" 0 0 0 0 0 -1 E E E -30 499 -"FRAME2" -"" -1 8 6 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E - E E -176 0 0 0 0 0 -E E E -1 -1 -LANG:1 0 - -1 -"dashclr"N "_Transparent" -E E 0 0 1 0 1 E 0.825 0 0.88888888888889 703.4 654.6666666666666 0 E 8 6 409 97 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 1 -LANG:1 5 Claim -13 504 -"PUSH_BUTTON17" -"" -1 1050 670 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E - E E -186 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -0 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 1048 668 1154 742 - -T -1 -LANG:1 29 Display -claims -( LogViewer ) -"main() -{ - // global dyn_string strClaimDPName; // datapoint that was claimed - // global dyn_string strClaimObjectName; // Actual object name - - DebugN( \"*********************************************\" ); - DebugN( \"Our global variable 'strClaimDPName' and 'strClaimObjectName' hold following records\" ); - - if( dynlen( strClaimObjectName )) - for( int t = 1; t <= dynlen( strClaimDPName ); t++) - { - DebugN( strClaimDPName[t] + \",\" + strClaimObjectName[t] ); - } - -}" 0 - E E E -30 547 -"FRAME3" -"" -1 8 6 E E E 1 E 1 E N "_WindowText" E N {0,0,0} E E - E E -188 0 0 0 0 0 -E E E -1 -1 -LANG:1 0 - -1 -"dashclr"N "_Transparent" -E E 0 0 1 0 1 E 1.1 0 0.6666666666666674 701.2000000000001 736 0 E 8 6 409 97 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 1 -LANG:1 8 Response -2 548 -"PRIMITIVE_TEXT3" -"" -1 721.9999999999993 764 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E - E E -190 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -1 -"dashclr"N "_Transparent" -E E 2 1 1 2 1 E 0.9999999999999999 0 1 0 0 1 E 722 764 802 788 -0 2 2 "0s" 0 0 0 64 0 0 722 764 1 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 1 -LANG:1 3 DP: -14 549 -"txt_response" -"" -1 769.9999999999998 764 E E E 1 E 1 E N "_WindowText" E N "_Window" E E - E E -192 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -0 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 768 762 1142 790 -3 "0s" 0 0 0 0 0 -1 E E E 13 592 "PUSH_BUTTON18" "" @@ -1370,36 +1169,6 @@ LANG:1 12 alarmmessage LANG:1 32 Arial Black,-1,11,5,50,0,0,0,0,0 0 18 792 304 815 3 "0s" 0 0 0 0 0 -1 E E E -13 834 -"PUSH_BUTTON21" -"" -1 830 710 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E - E E -209 0 0 0 0 0 -E E E -0 -1 -LANG:1 0 - -0 -1 -LANG:1 26 Arial,-1,17,5,50,0,0,0,0,0 -0 828 708 912 738 - -T -1 -LANG:1 4 free -"main() -{ - int ret = dpSet( - \"ClaimManager.reset.typeName\" , \"Observation\", - \"ClaimManager.reset.objectName\" , NAME.text ); - - if (ret < 0) DebugN(\"something went wrong resetting DP \"+NAME.txt+ \" \" + getLastError()); - - -}" 0 - E E E 13 883 "PUSH_BUTTON22" "" @@ -1465,11 +1234,11 @@ LANG:1 0 1 "$name""fw_topDetailSelection" 3 2 "PANEL_REF3" -1 -"objects\\Test\\Action.pnl" 668 220 T 107 1 0 1 -78 4 +"objects\\Test\\Action.pnl" 668 220 T 107 1 0 1 -78 -10 1 "$name""fw_bottomDetailSelection" 3 4 "PANEL_REF5" -1 -"objects\\Test\\Action.pnl" 649 450 T 108 1 0 1 -77 -3 +"objects\\Test\\Action.pnl" 649 450 T 108 1 0 1 -49 -40 1 "$name""fw_locator" 3 5 "PANEL_REF6" -1 @@ -1493,7 +1262,7 @@ LANG:1 0 1 "$name""fw_bottomDetailSelection" 3 14 "PANEL_REF15" -1 -"objects\\Test\\Event.pnl" 578 531 T 114 U +"objects\\Test\\Event.pnl" 578 531 T 114 1 0 1 12 -43 1 "$name""fw_locator" 3 17 "PANEL_REF18" -1 diff --git a/MAC/Navigator2/panels/Test/Navigator_testPanel.pnl b/MAC/Navigator2/panels/Test/Navigator_testPanel.pnl new file mode 100644 index 0000000000000000000000000000000000000000..021f0661b4969495f25e6661948f74e41aafda36 --- /dev/null +++ b/MAC/Navigator2/panels/Test/Navigator_testPanel.pnl @@ -0,0 +1,72 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 1205 862 N "_3DFace" 0 +E E E E E 1 -1 -1 0 150 150 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +28 0 +"TAB1" +"" +1 0 0 E E E 1 E 1 E N "_3DText" E N "_3DFace" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 -2 -2 1212 872 +E2 "Events" 1 +LANG:1 6 Events +1 "Test/Event_Viewer.pnl" 1 +LANG:1 0 +0 + +"Claims" 1 +LANG:1 6 Claims +1 "Test/Claim_Viewer.pnl" 1 +LANG:1 0 +0 + + +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/test.pnl b/MAC/Navigator2/panels/Test/test.pnl index c6942e2382e422309a050144de85e3d30d75a467..709011380dcb24edc4cbda372827839fee1666b0 100644 --- a/MAC/Navigator2/panels/Test/test.pnl +++ b/MAC/Navigator2/panels/Test/test.pnl @@ -5,56 +5,33 @@ LANG:6 0 PANEL,-1 -1 838 396 N "_3DFace" 0 "main() { - dyn_string statedps; - string observation = \"402147\"; - - string CEPDBNAME = \"CCU001\"; - string connectToStates = \"SELECT '_online.._value' FROM '{LOFAR**CobaltGPUProc*.status.state,LOFAR**CobaltGPUProc*.status.childState,LOFAR**CobaltStationInput.status.state,LOFAR**CobaltStationInput.status.childState}' REMOTE '\"+CEPDBNAME+\"' WHERE 'observationName:_online.._value' == '\"+observation+\"'\"; - - dyn_dyn_anytype aResult; - dpQuery(connectToStates,aResult); - -// DebugN(\"result: \", aResult); - - // Iterate through the results and collect the state and childState dps - dyn_string stateDPs; - for( int t = 2; t <= dynlen( aResult ); t++) + dpConnect(\"monitorRunStates\",\"ExampleDP_Arg1\"); + if (dpDisconnect(\"monitorRunStates\",\"ExampleDP_Arg1\") < 0) { - // skip the lines that contain the observationNames - string line = aResult[t][1]; - if (strpos(line,\"observationName\") >= 0) continue; - if (!dynContains(stateDPs, line) && dynlen(stateDPs) < 99) dynAppend(stateDPs,line); - } - - - // append the main hardware state - dynAppend(stateDPs,CEPDBName+\"LOFAR_PIC_Cobalt.status.state\"); - dynAppend(stateDPs,CEPDBName+\"LOFAR_PIC_Cobalt.status.childState\"); - - DebugN(\"connectCobaltNodesAndProcesses nr stateDPS to connect:\", dynlen(stateDPs)); - - dpConnect(\"CB\",TRUE,stateDPs); - - dyn_errClass err = getLastError(); //test whether an error occurred - if(dynlen(err) > 0) { - errorDialog(err); - // open dialog box with errors - throwError(err); // write errors to stderr - - LOG_ERROR(\"ObservationFlow_cobaltNodeProcesses.pnl:connectCobaltNodesAndProcesses| ERROR: connect fails:\"+ stateDPs); - } else { - DebugN(\" No error\"); - } + DebugN(\"Error 1\"); + } + else + { + DebugN(\"No error 1\"); + } + if (dpDisconnect(\"monitorRunStates\",\"ExampleDP_Arg1\") < 0) + { + DebugN(\"Error 2\"); + } + else + { + DebugN(\"No error 2\"); + } } -void CB(dyn_string dpList, dyn_int stateList) +void monitorRunStates(string dp, float value) { - DebugN(\"CB| has \" + dynlen( dpList) + \" results\" ); - DebugN(dpList); + DebugN(\"in runstates\"); }" 0 E E E E 1 -1 -1 0 50 30 ""0 1 -E E 2 +E "#uses \"navPanel.ctl\"" 0 + 2 "CBRef" "1" "EClose" E "" diff --git a/MAC/Navigator2/panels/Test/test3.pnl b/MAC/Navigator2/panels/Test/test3.pnl new file mode 100644 index 0000000000000000000000000000000000000000..9e7ad3fdc6bf4bb5f48544033b500f2b34591cab --- /dev/null +++ b/MAC/Navigator2/panels/Test/test3.pnl @@ -0,0 +1,57 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 500 400 N "_3DFace" 0 +"main() +{ +time resetDate,compareDate; + + resetDate=makeTime(2013,5,27); + compareDate = makeTime(2013,5,20); + + int now = period(resetDate); + int compare = period(compareDate); + DebugN(resetDate); + DebugN(compareDate); + DebugN(\"diff:\"+(now-compare)); +}" 0 + E E E E 1 -1 -1 0 -1 -1 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/testAlertRowLOFAR.pnl b/MAC/Navigator2/panels/Test/testAlertRowLOFAR.pnl new file mode 100644 index 0000000000000000000000000000000000000000..c2eaa03058d9ada9e5f50f83f60835a4ea863ecc --- /dev/null +++ b/MAC/Navigator2/panels/Test/testAlertRowLOFAR.pnl @@ -0,0 +1,131 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 1123 644 N "_3DFace" 0 +E E E E E 1 -1 -1 0 -1 19 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +29 17 +"sl_gauge" +"" +1 930 200.0000000000001 E E E 1 E 0 E N "_3DText" E N "_3DFace" E E + E E +1 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +8 +"invertedAppearance" "bool TRUE" +"maxValue" "int 99" +"tickInterval" "int 1" +"value" "int 50" +"backgroundOrigin" "enum 0" +"lineStep" "int 1" +"minValue" "int 1" +"invertedAppearance" "bool TRUE" +2 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +LANG:0 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 930 200 953 510 +12 SliderWidget +0 +E1 1 0 "0" 18 +0 +1 2 0 "0" 19 +0 +1 3 0 "0" 24 +0 +1 4 0 "0" 8 +0 +1 5 0 "0" 9 +0 +1 6 0 "0" 16 +0 +1 7 0 "0" 12 +0 +1 8 0 "0" 14 +0 +1 9 0 "0" 17 +0 +1 10 0 "0" 25 +0 +1 11 0 "0" 15 +0 +1 12 0 "0" 21 +0 +1 13 0 "0" 22 +0 +1 14 0 "0" 0 +0 +1 15 0 "0" 1 +31 "transform" 0 0 1 0 19.28571428571429 0 -914.2857142857145 +0 +13 16 +"PUSH_BUTTON1" +"" +1 40 10 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 38 8 192 42 + +T +1 +LANG:1 12 PUSH_BUTTON1 +"main() +{ + DebugN( \"AES_ACTION_AUTORUN = \" + AES_ACTION_AUTORUN ); + openAES( \"aes_alerts_LOFAR\", \"Alarmen\", AES_ACTION_AUTORUN ); +}" 0 + E E E +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +3 0 "PANEL_REF0" -1 +"objects\\STD_PANELS\\AESRow.pnl" -1 19 T 0 U +1 +"$AESREGDOLLAR_SCREENTYPE""aes_alertRow_LOFAR" +0 diff --git a/MAC/Navigator2/panels/Test/testClaim.pnl b/MAC/Navigator2/panels/Test/testClaim.pnl index 87db647f2dc851c3769771b6fd6d91c606b41038..4d9b61acdf87c7a53c013f909c2ffd9ac580c713 100644 --- a/MAC/Navigator2/panels/Test/testClaim.pnl +++ b/MAC/Navigator2/panels/Test/testClaim.pnl @@ -1,7 +1,7 @@ V 11 1 LANG:1 0 -PANEL,-1 -1 855 180 N "_3DFace" 0 +PANEL,-1 -1 1205 180 N "_3DFace" 0 "main() { dyn_string dps = makeDynString(); @@ -62,7 +62,7 @@ LANG:1 0 25 0 "claimtable" "" -1 9.999999999999911 19.99999999999998 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 9.999999999999874 19.99999999999998 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 0 0 0 0 0 0 E E E @@ -73,32 +73,32 @@ LANG:1 0 0 1 LANG:1 30 Sans Serif,9,-1,5,50,0,0,0,0,0 -0 8 18 832 162 +0 8 18 1182 162 EE 1 0 1 4 0 "temp" 21 1 0 "s" 1 LANG:1 4 temp E 1 LANG:1 0 -200 "name" 21 1 0 "s" 1 +400 "name" 21 1 0 "s" 1 LANG:1 4 name E 1 LANG:1 0 -200 "claim" 21 1 0 "s" 1 +400 "claim" 21 1 0 "s" 1 LANG:1 5 claim E 1 LANG:1 0 -200 "free" 21 1 0 "s" 1 +150 "free" 21 1 0 "s" 1 LANG:1 4 free E 1 LANG:1 0 -200 +150 14 14 10 10 1 LANG:1 30 Sans Serif,9,-1,5,50,0,0,0,0,0 diff --git a/MAC/Navigator2/panels/Test/testDynDynSort.pnl b/MAC/Navigator2/panels/Test/testDynDynSort.pnl new file mode 100644 index 0000000000000000000000000000000000000000..16888a82fcd12d48864aa106fbda8ac8dc37f016 --- /dev/null +++ b/MAC/Navigator2/panels/Test/testDynDynSort.pnl @@ -0,0 +1,72 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 400 400 N "_3DFace" 0 +E E E E E 1 -1 -1 0 200 210 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +13 0 +"PUSH_BUTTON1" +"" +1 40 40 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 34 MS Shell Dlg 2,8,-1,5,50,0,0,0,0,0 +0 38 38 162 64 + +T +1 +LANG:1 12 PUSH_BUTTON1 +E "main() { + string aS = \"CS001:__navObjectState.DPName\"; +// string aS = \"CS001:LOFAR_PIC_Cabinet1_Subrack2.clockBoard.status.state\"; + string System = dpSubStr(aS,DPSUB_SYS); + DebugN(System); + System = dpSubStr(\"\",DPSUB_SYS); + DebugN(System); +}" 0 + E E +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/testEWO.pnl b/MAC/Navigator2/panels/Test/testEWO.pnl new file mode 100644 index 0000000000000000000000000000000000000000..c80b33a258a2cbf979bd8e6ed7a581b308fa2df9 --- /dev/null +++ b/MAC/Navigator2/panels/Test/testEWO.pnl @@ -0,0 +1,61 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 1274 644 N "_3DFace" 0 +E E E E E 1 -1 -1 0 70 30 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +29 0 +"Scheduler_ewo1" +"" +1 70 30 E E E 1 E 1 E N "_3DText" E N "_3DFace" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 70 30 1170 560 +13 Scheduler.ewo +0 +E0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/testStationSumAlertToMCUalert.pnl b/MAC/Navigator2/panels/Test/testStationSumAlertToMCUalert.pnl new file mode 100644 index 0000000000000000000000000000000000000000..0174696c805d062139f346f6c8014519af7fcad5 --- /dev/null +++ b/MAC/Navigator2/panels/Test/testStationSumAlertToMCUalert.pnl @@ -0,0 +1,177 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 500 400 N "_3DFace" 0 +"main() +{ + Init(); +}" 0 + E E E E 1 -1 -1 0 40 30 +""0 1 +E "string _strCurrentAlertClass; + + +void Init() +{ +// string strQuery = \"SELECT ALERT '_alert_hdl.._direction', '_alert_hdl.._prior', '_alert_hdl.._class' FROM 'CS011_SumAlert'\"; +// dpQueryConnectSingle( \"CallbackAlertMCU\", true, \"\", strQuery ); + + +// string strQuery = \"SELECT ALERT '_alert_hdl.._direction', '_alert_hdl.._prior', '_alert_hdl.._class' FROM 'LOFAR.status.childSumAlert' REMOTE 'CS011:'\"; + string strQuery = \"SELECT ALERT '_alert_hdl.._direction', '_alert_hdl.._prior', '_alert_hdl.._class' FROM 'LOFAR_PIC_Cabinet1_Subrack2.status.childSumAlert' REMOTE 'CS011:'\"; + dpQueryConnectSingle( \"CallbackAlertLCU\", true, \"\", strQuery ); +} + +/* +void CallbackAlertMCU( string strIdent, dyn_dyn_anytype ddaAlerts ) +{ + DebugN( \"CallbackAlert:\" ); + DebugN( ddaAlerts ); + + for( x=2; x<=dynlen(ddaAlerts); x++ ) + { + strClass = ddaAlerts[x][5]; + + // Skip went alerts + if( !bDirection ) + continue; + + // Came alert, if prio heigher then takeover alertclass + if( iPrio >= iHighestPrio ) + { + _strCurrentAlertClass = strClass; + + iHighestPrio = iPrio; + } + } + + +} + +*/ + + +void CallbackAlertLCU( string strIdent, dyn_dyn_anytype ddaAlerts ) +{ + int x, iHighestPrio; + bool bCame, bRetVal; + string strHighestAlertClass; + + DebugN( \"CallbackAlertLCU:\" ); + DebugN( ddaAlerts ); + + for( x=2; x<=dynlen(ddaAlerts); x++ ) + { + bool bDirection = ddaAlerts[x][3]; + int iPrio = ddaAlerts[x][4]; + string strClass = ddaAlerts[x][5]; + + // Skip went alerts + if( !bDirection ) + continue; + + bCame = true; + + // Came alert, if prio heigher then takeover alertclass + if( iPrio >= iHighestPrio ) + { + strHighestAlertClass = strClass; + + iHighestPrio = iPrio; + } + } + + DebugN( \"strHighestAlertClass = \" + strHighestAlertClass ); + + // Now get state of alert + if( bCame ) + { + // Convert class from system to this sytem + strreplace( strHighestAlertClass, \"CS011:\", \"MCU001:\" ); + + DebugTN( \"Setting alert to class: \" + strHighestAlertClass ); + + dpDeactivateAlert( \"MCU001:CS011_SumAlert.\", bRetVal ); + + // Change class and set alert to true + dpSet( \"MCU001:CS011_SumAlert.:_alert_hdl.._class\", strHighestAlertClass, + \"MCU001:CS011_SumAlert.\", true ); + + dpActivateAlert( \"MCU001:CS011_SumAlert.\", bRetVal ); + } + else + { + DebugTN( \"Reset alert\" ); + dpDeactivateAlert( \"MCU001:CS011_SumAlert.\", bRetVal ); + dpSet( \"MCU001:CS011_SumAlert.\", false ); + } +} + + + + +" 0 + 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +13 1 +"PUSH_BUTTON2" +"" +1 100 50 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +1 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 98 48 332 152 + +T +1 +LANG:1 12 PUSH_BUTTON2 +"main() +{ + int iState; + dpGet( \"MCU001:CS011_SumAlert.:_alert_hdl.._act_state\", iState ); + DebugN( \"iState = \" + iState ); +}" 0 + E E E +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/Test/testclick.pnl b/MAC/Navigator2/panels/Test/testclick.pnl index 8e9cc218043050a202186abe49e0a3038dde1286..0c0d9d013ae6e8c011cb275430df8ba5abf2e9a9 100644 --- a/MAC/Navigator2/panels/Test/testclick.pnl +++ b/MAC/Navigator2/panels/Test/testclick.pnl @@ -7,30 +7,11 @@ PANEL,-1 -1 500 408 N "_3DFace" 0 { - dyn_dyn_string dd_string; - - - - dyn_string d_string1=makeDynString(\"c\",\"a\",\"b\"); - - dyn_string d_string2=makeDynString(\"1\",\"3\",\"2\"); - - dyn_string d_string3=makeDynString(\"wel\",\"dit\",\"klopt\"); - - - - dd_string[1]=d_string1; - - dd_string[2]=d_string2; - - dd_string[3]=d_string3; - - dynSort(d_string1); - DebugN(d_string1); - - dynDynSort(dd_string, 1); // asc - - DebugN(dd_string); +// string sys = (strsplit(getSystemName(),\":\"))[1]+\"c.control.lofar\" + string sys = (strsplit(getSystemName(),\":\"))[1]+\".control.lofar\"; + DebugN(sys); + + DebugN(getHostByName(sys)); }" 0 E E E E 1 -1 -1 0 50 40 diff --git a/MAC/Navigator2/panels/Test/testdptypename.pnl b/MAC/Navigator2/panels/Test/testdptypename.pnl new file mode 100644 index 0000000000000000000000000000000000000000..de610524830ad284aac3741d8b728342c01852a4 --- /dev/null +++ b/MAC/Navigator2/panels/Test/testdptypename.pnl @@ -0,0 +1,84 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 500 400 N "_3DFace" 0 +E E E E E 1 -1 -1 0 40 50 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +14 0 +"TEXT_FIELD1" +"" +1 40 50 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 38 48 472 82 +3 "0s" 0 0 0 0 0 -1 E E E +13 1 +"PUSH_BUTTON1" +"" +1 120 170 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +1 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 118 168 242 194 + +T +1 +LANG:1 12 PUSH_BUTTON1 +"main() +{ + TEXT_FIELD1.text = dpTypeName(TEXT_FIELD1.text); +}" 0 + E E E +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/main.pnl b/MAC/Navigator2/panels/main.pnl index 5c68aea3f973ed2c260f490f6547d2e40acdb411..aeefb4403926721c61bd36e0b040562a00635831 100644 --- a/MAC/Navigator2/panels/main.pnl +++ b/MAC/Navigator2/panels/main.pnl @@ -523,26 +523,26 @@ LANG:1 5 T H 2 46 "PRIMITIVE_TEXT18" "" -1 747 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 750 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 31 0 0 0 0 0 E E E 0 1 -LANG:1 27 power 48V and TBB.recording +LANG:1 39 power 48V power unit, and TBB.recording 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E U 0 E 747 10 785 23 -0 2 2 "0s" 0 0 0 192 0 0 747 10 1 +E E 0 1 1 2 1 E U 0 E 750 10 820 23 +0 2 2 "0s" 0 0 0 192 0 0 750 10 1 1 LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 0 1 -LANG:1 6 48 TBB +LANG:1 12 48 Pow TBB 2 47 "PRIMITIVE_TEXT19" "" -1 830 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 870 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 32 0 0 0 0 0 E E E @@ -552,8 +552,8 @@ LANG:1 26 #faulty HBA & LBA Antennas 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E U 0 E 830 10 877 23 -0 2 2 "0s" 0 0 0 192 0 0 830 10 1 +E E 0 1 1 2 1 E U 0 E 870 10 917 23 +0 2 2 "0s" 0 0 0 192 0 0 870 10 1 1 LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 0 1 @@ -732,7 +732,7 @@ LANG:1 14 processSubtype 2 117 "PRIMITIVE_TEXT39" "" -1 940 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 970 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 54 0 0 0 0 0 E E E @@ -742,8 +742,8 @@ LANG:1 26 #faulty HBA & LBA Antennas 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E U 0 E 940 10 1019 23 -0 2 2 "0s" 0 0 0 192 0 0 940 10 1 +E E 0 1 1 2 1 E U 0 E 970 10 1049 23 +0 2 2 "0s" 0 0 0 192 0 0 970 10 1 1 LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 0 1 @@ -751,7 +751,7 @@ LANG:1 13 input streams 2 172 "PRIMITIVE_TEXT40" "" -1 790 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E +1 830 10 E E E 1 E 1 E N "_WindowText" E N "_Window" E E E E 65 0 0 0 0 0 E E E @@ -761,8 +761,8 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 2 1 E U 0 E 790 10 823 23 -0 2 2 "0s" 0 0 0 192 0 0 790 10 1 +E E 0 1 1 2 1 E U 0 E 830 10 863 23 +0 2 2 "0s" 0 0 0 192 0 0 830 10 1 1 LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 0 1 diff --git a/MAC/Navigator2/panels/objects/Alerts/alarmsWinCCOA.pnl b/MAC/Navigator2/panels/objects/Alerts/alarmsWinCCOA.pnl new file mode 100644 index 0000000000000000000000000000000000000000..ad816b45328fdfedfbac4af93349ddf7158b3f5a --- /dev/null +++ b/MAC/Navigator2/panels/objects/Alerts/alarmsWinCCOA.pnl @@ -0,0 +1,116 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 1242 823 N "_3DFace" 0 +E E E E E 1 -1 -1 0 10 10 +""0 1 +E E 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +1 1 0 "" 26 +0 +1 2 0 "" 27 +0 +1 3 0 "" 0 +0 +1 4 0 "" 1 +0 +1 5 0 "" 17 +0 +1 6 0 "" 19 +0 +1 7 0 "" 20 +0 +1 8 0 "" 21 +0 +1 9 0 "" 25 +0 +1 10 0 "0" 0 +0 +1 11 0 "0" 1 +0 +1 12 0 "" 11 +0 +1 13 0 "" 12 +0 +1 14 0 "" 15 +0 +1 15 0 "" 14 +0 +1 16 0 "" 16 +0 +29 17 +"BackgroundCover_ewo1" +"" +1 710.0000000000003 753 E E E 1 E 1 E N "_3DText" E N {170,170,170} E E + E E +5 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 710 753 1078 793 +19 BackgroundCover.ewo +0 +E29 18 +"BackgroundCover_ewo2" +"" +1 400 680 E E E 1 E 1 E N "_3DText" E N {240,240,240} E E + E E +6 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 400 680 768 720 +19 BackgroundCover.ewo +0 +E0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +3 0 "PANEL_REF0" -1 +"vision\\aes\\AEScreen.pnl" 118 1270 T 4 1 0 1 -20 -40 +3 +"$ACTION""1" +"$FILENAME""" +"$SCREENTYPE""aes_alerts_LOFAR" +0 diff --git a/MAC/Navigator2/panels/objects/Hardware/RSPBoard_AP.pnl b/MAC/Navigator2/panels/objects/Hardware/RSPBoard_AP.pnl index 6b133fc226359d9a71c456e4bbad2693e0c9b1f3..8835eb5411a948d60c6e180c2c01e32a7eff00f6 100644 --- a/MAC/Navigator2/panels/objects/Hardware/RSPBoard_AP.pnl +++ b/MAC/Navigator2/panels/objects/Hardware/RSPBoard_AP.pnl @@ -1,4 +1,4 @@ -V 10 +V 11 1 LANG:1 10 Subrack_AP PANEL,-1 -1 184 193 N "_3DFace" 1 @@ -49,9 +49,9 @@ E "#uses \"navPanel.ctl\" string baseDP=\"\"; -FPGASyncDetails(string dp1, int sampleCount, - string dp2, int syncCount, - string dp3, int errorCount) +FPGASyncDetails(string dp1, uint sampleCount, + string dp2, uint syncCount, + string dp3, uint errorCount) { setValue(\"output_errorCount\" , \"text\", errorCount); setValue(\"output_sampleCount\", \"text\", sampleCount); @@ -106,12 +106,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 0 1 E U 0 E 27 33 45 50 +E E 0 1 1 0 1 E U 0 E 27 33 45 49 0 2 0 "0s" 0 0 0 192 0 0 27 33 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-13-*-100-100-*-*-iso8859-1|-13,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +0 1 LANG:1 2 AP 2 6 "txt_version" @@ -126,12 +125,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 0 1 E U 0 E 121 166 158 181 +E E 0 1 1 0 1 E U 0 E 121 166 158 180 0 2 0 "0s" 0 0 0 194 0 0 158 166 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-11-*-100-100-*-*-iso8859-1|-11,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,11,5,40,0,0,0,0,0 +0 1 LANG:1 8 ver: x.x 2 8 "label_sampleCount" @@ -149,9 +147,8 @@ LANG:1 0 E E 0 1 1 0 1 E U 0 E 22 97 72 116 0 2 0 "0s" 0 0 0 66 0 0 22 97 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-12-*-100-100-*-*-iso8859-1|-12,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,12,5,40,0,0,0,0,0 +0 1 LANG:1 7 Sample: 2 9 "label_syncCount" @@ -169,9 +166,8 @@ LANG:1 0 E E 0 1 1 0 1 E U 0 E 39 120 72 136 0 2 0 "0s" 0 0 0 66 0 0 39 120 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-12-*-100-100-*-*-iso8859-1|-12,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,12,5,40,0,0,0,0,0 +0 1 LANG:1 5 Sync: 2 7 "label_errorCount" @@ -189,9 +185,8 @@ LANG:1 0 E E 0 1 1 0 1 E U 0 E 39 143 72 159 0 2 0 "0s" 0 0 0 66 0 0 39 143 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-12-*-100-100-*-*-iso8859-1|-12,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,12,5,40,0,0,0,0,0 +0 1 LANG:1 6 Error: 2 18 "txt_temperature" @@ -206,12 +201,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 0 1 E U 0 E 126 32 133 49 +E E 0 1 1 0 1 E U 0 E 126 32 133 48 0 2 0 "3.0f" 4 0 0 194 0 0 133 32 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-13-*-100-100-*-*-iso8859-1|-13,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +0 1 LANG:1 1 x 2 4 "label_temperature" @@ -226,12 +220,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 1 1 0 1 E U 0 E 134 32 148 49 +E E 0 1 1 0 1 E U 0 E 134 32 148 48 0 2 0 "0s" 0 0 0 194 0 0 148 32 1 1 -LANG:1 84 -*-Arial-*-r-normal-*-13-*-100-100-*-*-iso8859-1|-13,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" -1 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +0 1 LANG:1 2 �C 1 19 0 "" 1 0 @@ -248,9 +241,8 @@ LANG:1 0 0 1 -LANG:1 84 -*-Arial-*-r-normal-*-11-*-100-100-*-*-iso8859-1|-11,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" - 78 139 152 164 +LANG:1 26 Arial,-1,11,5,40,0,0,0,0,0 +0 78 139 152 164 3 "0s" 0 0 0 2 0 -1 E E E 14 13 "output_sampleCount" @@ -265,9 +257,8 @@ LANG:1 0 0 1 -LANG:1 84 -*-Arial-*-r-normal-*-11-*-100-100-*-*-iso8859-1|-11,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" - 78 90 152 115 +LANG:1 26 Arial,-1,11,5,40,0,0,0,0,0 +0 78 90 152 115 3 "0s" 0 0 0 2 0 -1 E E E 14 14 "output_syncCount" @@ -282,9 +273,8 @@ LANG:1 0 0 1 -LANG:1 84 -*-Arial-*-r-normal-*-11-*-100-100-*-*-iso8859-1|-11,0,0,0,404,0,0,0,0,0,0,0,0,Arial -0 "" - 78 114 152 139 +LANG:1 26 Arial,-1,11,5,40,0,0,0,0,0 +0 78 114 152 139 3 "0s" 0 0 0 2 0 -1 E E E 0 LAYER, 1 @@ -315,7 +305,7 @@ LAYER, 7 1 LANG:1 6 Layer8 0 -3 0 "selfState" +3 0 "selfState" -1 "objects\\lofar_self_state.pnl" 25 171 T 17 1 0 1 -2 -15 0 -0 \ No newline at end of file +0 diff --git a/MAC/Navigator2/panels/objects/Hardware/Station_PowerUnit.pnl b/MAC/Navigator2/panels/objects/Hardware/Station_PowerUnit.pnl new file mode 100644 index 0000000000000000000000000000000000000000..f7752a65c48da0da3aea4c7c11d6964b9241279a --- /dev/null +++ b/MAC/Navigator2/panels/objects/Hardware/Station_PowerUnit.pnl @@ -0,0 +1,504 @@ +V 11 +1 +LANG:1 8 (NoName) +PANEL,-1 -1 1063 427 N "_3DFace" 1 +"$unitNumber" +"main() +{ + powerunitNr = $unitNumber; + baseDP = sysName+\"LOFAR_PIC_POWEC\" + powerunitNr; + + if (dpGet(sysName+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\",nrOfPowerUnits) == -1) + { + LOG_TRACE(\"Station_PowerUnits.pnl:prepareHardwareList|failed to get \"+sysName+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\"); + return; + } + + // check if this powerunit is available in the system and needs to be filled needs to be filled + if (powerunitNr >= nrOfPowerUnits ) + { + this.enabled(false); + border.visible(false); + setValue(\"selfState\", \"visible\", false); + setValue(\"powerunit_highlight\", \"visible\", false); + unitname.visible(false); + valueText.visible(false); + alarmText.visible(false); + powerValueTable.visible(false); + powerAlarmTable.visible(false); + return; + } + + if(dpExists(baseDP +\".nrOfModules:_online.._value\")) + { + dpConnect(\"setPUvalues\", baseDP +\".nrOfModules:_online.._value\", + baseDP +\".voltage:_online.._value\", + baseDP +\".current:_online.._value\", + baseDP +\".temperature:_online.._value\", + baseDP +\".OK:_online.._value\", + baseDP +\".nrOfModules:_online.._invalid\"); + dpConnect(\"setPUalarms\", baseDP +\".nrOfAlarms:_online.._value\", + baseDP +\".alarmTime:_online.._value\", + baseDP +\".alarmText:_online.._value\", + baseDP +\".alarmType:_online.._value\", + baseDP +\".alarmReason:_online.._value\", + baseDP +\".nrOfAlarms:_online.._invalid\"); + if (!navFunct_dpReachable(baseDP)) + { + setPUvalues(\"\",0, + \"\",makeDynInt(), + \"\",makeDynInt(), + \"\",makeDynInt(), + \"\",makeDynInt(), + \"\",true); + setPUalarms(\"\",0, + \"\",makeDynString(), + \"\",makeDynString(), + \"\",makeDynInt(), + \"\",makeDynInt(), + \"\",true); + } + } + + + setValue(\"unitname\",\"text\",\"POWEC: \" + powerunitNr); + + // pass baseDP to selfstate Object to work with + setValue(\"selfState.light\",\"toolTipText\",baseDP); + + if (dpExists(baseDP) ){ + // connect for selfUpdates + showSelfState(baseDP); + } +} + +void setPUvalues(string dp1, int nrOfModules, + string dp2, dyn_int voltage, + string dp3, dyn_int current, + string dp4, dyn_int temperature, + string dp5, dyn_int OK, + string dp6, bool invalid) +{ + // clear the table + powerValueTable.deleteAllLines(); + if (!invalid) + { + powerValueTable.backCol(\"_Transparent\"); + // fill the table + for (int i = 1; i <= nrOfModules; i++) + { + // sometimes the number of values for one or more single entries isn't the same as the number of available modules. + // we want to be able to see the remaining correct values, so we have to skip those + float v = 0.0; + float c = 0.0; + int t = 0; + int s = 0; + if (i <= dynlen(voltage)) v = voltage[i]/10.; + if (i <= dynlen(current)) c = current[i]/10.; + if (i <= dynlen(temperature)) t = temperature[i]; + if (i <= dynlen(OK)) s = OK[i]; + + + powerValueTable.appendLine(\"Voltage\",v,\"Current\",c,\"Temperature\",t,\"Status\",s); + } + } + else + { + setValue(\"unitname\",\"text\",\"POWEC: \" + powerunitNr + \" INVALID\"); + powerValueTable.foreCol(\"Lofar_invalid\"); + } +} + +void setPUalarms(string dp1, int nrOfAlarms, + string dp2, dyn_string alarmtime, + string dp3, dyn_string alarmtext, + string dp4, dyn_int alarmtype, + string dp5, dyn_int alarmreason, + string dp6, bool invalid) +{ + // clear the table + powerAlarmTable.deleteAllLines(); + if (!invalid) + { + powerAlarmTable.backCol(\"_Transparent\"); + // fill the table + for (int i = 1; i <= nrOfAlarms; i++) + { + // sometimes the number of values for one or more single entries isn't the same as the number of available modules. + // we want to be able to see the remaining correct values, so we have to skip those + string ti = \"\"; + string tx = \"\"; + int tp = 0; + int r = 0; + if (i <= dynlen(alarmtime)) ti = alarmtime[i]; + if (i <= dynlen(alarmtext)) tx = alarmtext[i]; + if (i <= dynlen(alarmtype)) tp = alarmtype[i]; + if (i <= dynlen(alarmreason)) r = alarmreason[i]; + + powerAlarmTable.appendLine(\"Time\",ti,\"Text\",tx,\"Type\",tp,\"Reason\",r); + } + } + else + { + powerAlarmTable.foreCol(\"Lofar_invalid\"); + } +}" 0 + E "main() +{ + click(); +}" 0 + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + 1 0 0 0 17 191 +""0 1 +E "#uses \"navPanel.ctl\" + +string baseDP = \"\"; +int powerunitNr = -1; +bool bDoubleClicked = false; +int nrOfPowerUnits = 1; + +// routine for single mouse click +void click() { + // set delay in case double click was meant + delay(0, 100); + if (!bDoubleClicked) { + navPanel_setEvent(\"POWEC\"+powerunitNr,\"EventClick\"); + } +} + +// routine for double mouse click +void dblClick() { + // indicate this is a doubleClick + bDoubleClicked = true; + + if (dpExists(baseDP) ) { + LOG_DEBUG(\"Station_PowerUnit:DoubleClick|Setting currentDatapoint from : \"+g_currentDatapoint+\" to \"+baseDP); + g_currentDatapoint=baseDP; + navPanel_setEvent(\"POWEC\"+powerunitNr,\"ChangePanel\"); + } + + // set delay to avoid click event will be triggered + delay(0, 500); + bDoubleClicked = false; +} + +// routine for right mouse click +void rClick() { + navPanel_setEvent(\"POWEC\"+powerunitNr,\"EventRightClick\"); +}" 0 + 2 +"CBRef" "1" +"EClose" E +"" +1 +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 6 Layer1 +6 924 +"powerunit_highlight" +"" +1 110 90 E E E 1 E 0 E N "_Transparent" E N "Lofar_highLight" E E + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + +97 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +"main() +{ + dpConnect( \"PowerunitCallback\",true,DPNAME_NAVIGATOR + g_navigatorID +\".objectTrigger\" ); +} + +void PowerunitCallback(string dp1, bool aTrig) { + + LOG_DEBUG(\"Station_Powerunit.pnl:PowerunitCallback| ObjectTrigger Callback on: \"+dp1+\" trigger: \"+aTrig); + LOG_DEBUG(\"Station_Powerunit.pnl:PowerunitCallback|Found highlight : \" + highlight + \" Looking for: POWEC\" + powerunitNr); + bool bHighlight=false; + if (dynContains(highlight,\"POWEC\"+powerunitNr)) { + bHighlight=true; + } + LOG_DEBUG(\"Station_Powerunit.pnl:PowerunitCallback|Highlight request: \"+bHighlight); + powerunit_highlight.visible = bHighlight; +}" 0 + "main() +{ + click(); +}" 0 + 0 1 1 2 1 E U 1 E 127 97 957 307 +6 916 +"border" +"" +1 110 100 E E E 1 E 1 E N {0,0,0} E N "_3DFace" E E + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + +91 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E "main() +{ + click(); +}" 0 + 0 1 1 2 1 E 1.138888888888889 0 1 6.7222222222223 2 1 E 110 100 830 300 +1 925 1 "" 1 +0 +2 917 +"unitname" +"" +1 132 82 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + E E +92 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 2 1 E U 0 E 132 82 214 98 +0 2 2 "0s" 0 0 0 192 0 0 132 82 1 +1 +LANG:1 35 MS Shell Dlg 2,10,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 12 Power Unit 0 +25 918 +"powerValueTable" +"" +1 142 142 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E + "main(int row, string column) +{ + rClick(); +}" 0 + "main(int row, string column) +{ + dblClick(); +}" 0 + +93 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 34 MS Shell Dlg 2,8,-1,5,50,0,0,0,0,0 +0 140 140 424 284 +E"main(int row, string column, string value) +{ + click(); + +}" 0 + 1 0 1 4 0 "Voltage" 6 1 0 "[3.2f,False,False,ALIGNMENT_END,False]" 1 +LANG:1 7 Voltage +E +1 +LANG:1 0 + +60 "Current" 6 1 0 "[3.2f,False,False,ALIGNMENT_END,False]" 1 +LANG:1 7 Current +E +1 +LANG:1 0 + +60 "Temperature" 8 1 0 "[3.2f,False,False,ALIGNMENT_END,False]" 1 +LANG:1 11 Temperature +E +1 +LANG:1 0 + +74 "Status" 6 1 0 "[5s,,,ALIGNMENT_CENTER]" 1 +LANG:1 6 Status +E +1 +LANG:1 0 + +60 +15 15 10 10 +1 +LANG:1 34 MS Shell Dlg 2,8,-1,5,50,0,0,0,0,0 +0 0 1 1 1 7 +1 0 +25 920 +"powerAlarmTable" +"" +1 472 142 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E + "main(int row, string column) +{ + rClick(); +}" 0 + "main(int row, string column) +{ + dblClick(); +}" 0 + +94 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 470 140 934 284 +E"main(int row, string column, string value) +{ + click(); + +}" 0 + 1 0 1 4 0 "Time" 11 1 0 "[0s,,,ALIGNMENT_CENTER]" 1 +LANG:1 4 Time +E +1 +LANG:1 0 + +100 "Text" 30 1 0 "[0s,,,ALIGNMENT_BEGINNING]" 1 +LANG:1 4 Text +E +1 +LANG:1 0 + +250 "Type" 2 1 0 "[0d,False,False,ALIGNMENT_BEGINNING,False]" 1 +LANG:1 4 Type +E +1 +LANG:1 0 + +25 "Reason" 5 1 0 "[0d,False,False,ALIGNMENT_BEGINNING,False]" 1 +LANG:1 6 Reason +E +1 +LANG:1 0 + +50 +15 15 10 10 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 0 1 1 1 7 +1 0 +2 921 +"valueText" +"" +1 220 120 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + +95 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E "main() +{ + click(); +}" 0 + 0 1 1 2 1 E U 0 E 220 120 257 133 +0 2 2 "0s" 0 0 0 192 0 0 220 120 1 +1 +LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 6 Values +2 922 +"alarmText" +"" +1 662 122 E E E 1 E 1 E N "_WindowText" E N "_Window" E E + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + +96 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E "main() +{ + click(); +}" 0 + 0 1 1 2 1 E U 0 E 662 122 702 135 +0 2 2 "0s" 0 0 0 192 0 0 662 122 1 +1 +LANG:1 34 MS Shell Dlg 2,8,-1,5,75,0,0,0,0,0 +0 1 +LANG:1 6 Alarms +0 +LAYER, 1 +1 +LANG:1 6 Layer2 +0 +LAYER, 2 +1 +LANG:1 6 Layer3 +0 +LAYER, 3 +1 +LANG:1 6 Layer4 +0 +LAYER, 4 +1 +LANG:1 6 Layer5 +0 +LAYER, 5 +1 +LANG:1 6 Layer6 +0 +LAYER, 6 +1 +LANG:1 6 Layer7 +0 +LAYER, 7 +1 +LANG:1 6 Layer8 +0 +3 1 "selfState" -1 +"objects\\lofar_self_state.pnl" 363 291 T 87 1 0 1 -223 -182 +0 +0 diff --git a/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl b/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl index 62cc8cdb057f16ac55021658dd73c45486d0c7dd..2d103d58362a571af8ef40e172d2ebd6e1f92478 100644 --- a/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl +++ b/MAC/Navigator2/panels/objects/Hardware/Station_mainView.pnl @@ -1,7 +1,7 @@ V 11 1 LANG:1 0 -PANEL,-1 -1 380 26 N "_3DFace" 1 +PANEL,-1 -1 380 22 N "_3DFace" 1 "$station" "main() { @@ -81,6 +81,8 @@ LANG:1 0 0 1 37 13 "" 0 0 +1 38 14 "" 0 +0 0 LAYER, 1 1 @@ -120,17 +122,17 @@ LANG:1 0 "$fullDP""LOFAR_PIC_StationInfo.power48On" "$station""$station" 3 4 "PANEL_REF4" -1 -"objects\\Hardware\\antennaBroken_Small.pnl" 80 0 T 37 1 0 1 82 0 +"objects\\Hardware\\antennaBroken_Small.pnl" 80 0 T 37 1 0 1 112 0 2 "$antennaType""HBA" "$station""$station" 3 5 "PANEL_REF5" -1 -"objects\\Hardware\\antennaBroken_Small.pnl" 100 0 T 38 1 0 1 90 0 +"objects\\Hardware\\antennaBroken_Small.pnl" 100 0 T 38 1 0 1 120 0 2 "$antennaType""LBA" "$station""$station" 3 6 "PANEL_REF6" -1 -"objects\\Hardware\\TBBmode_small.pnl" 60 0 T 39 1 0 1 40 0 +"objects\\Hardware\\TBBmode_small.pnl" 60 0 T 39 1 0 1 70 0 1 "$station""$station" 3 7 "PANEL_REF7" -1 @@ -142,27 +144,31 @@ LANG:1 0 1 "$station""$station" 3 9 "PANEL_REF9" -1 -"objects\\Processes\\stationStreamView_small.pnl" 250 0 T 41 1 0 1 20 0 +"objects\\Processes\\stationStreamView_small.pnl" 250 0 T 41 1 0 1 50 0 2 "$station""$station" "$streamNr""0" 3 10 "PANEL_REF10" -1 -"objects\\Processes\\stationStreamView_small.pnl" 270 0 T 42 1 0 1 20 0 +"objects\\Processes\\stationStreamView_small.pnl" 270 0 T 42 1 0 1 50 0 2 "$station""$station" "$streamNr""1" 3 11 "PANEL_REF11" -1 -"objects\\Processes\\stationStreamView_small.pnl" 290 0 T 43 1 0 1 20 0 +"objects\\Processes\\stationStreamView_small.pnl" 290 0 T 43 1 0 1 50 0 2 "$station""$station" "$streamNr""2" 3 12 "PANEL_REF12" -1 -"objects\\Processes\\stationStreamView_small.pnl" 310 0 T 44 1 0 1 20 0 +"objects\\Processes\\stationStreamView_small.pnl" 310 0 T 44 1 0 1 50 0 2 "$station""$station" "$streamNr""3" 3 13 "PANEL_REF13" -1 -"objects\\Hardware\\AARTFAAC_small.pnl" 130 0 T 44 U +"objects\\Hardware\\AARTFAAC_small.pnl" 130 0 T 44 1 0 1 30 0 +1 +"$station""$station" +3 14 "PANEL_REF14" -1 +"objects/Hardware\\powerUnit_small.pnl" 100 0 T 44 U 1 "$station""$station" 0 diff --git a/MAC/Navigator2/panels/objects/Hardware/observationFlow_stations.pnl b/MAC/Navigator2/panels/objects/Hardware/observationFlow_stations.pnl index 9c0778f88200843c1333008ae20b02c2d22bd5d4..8aa7f77e09a44a0b8e9e02e7b6e4fc0af51a830a 100644 --- a/MAC/Navigator2/panels/objects/Hardware/observationFlow_stations.pnl +++ b/MAC/Navigator2/panels/objects/Hardware/observationFlow_stations.pnl @@ -169,9 +169,6 @@ void updateObservations(dyn_string dps,dyn_dyn_string values) { } dynSort(stationDPList); dynSort(stationList); - string ttip = \"Stations: <\\n>\"; - for (int i=1; i<= dynlen(stationList);i++) ttip += stationList[i]+\"<\\n>\"; - setValue(\"childStateBorder\",\"toolTipText\",ttip); if (dynlen(stationDPList) < 1) { setStates(0,0); @@ -189,10 +186,84 @@ void updateStationStates(dyn_string dps,dyn_string values) { int highestState=0; int highestChildState=0; + // keep stations that are not operational, but should be according to the observationlist + dyn_string maintenanceList; + dyn_string testList; + dyn_string suspiciousList; + dyn_string errorList; + dyn_string offlineList; + for (int i= 1; i<= dynlen(dps); i++) { - if (strpos(dps[i],\"status.state\") >= 0 && values[i] > highestState) highestState=values[i]; + if (strpos(dps[i],\"status.state\") >= 0) + { + int val = values[i]; + if ( val > highestState) highestState=val; + string station = dpSubStr(dps[i],DPSUB_SYS); + if (val >= 60) + { + dynAppend(offlineList,navFunct_bareDBName(station)); + } + else if (val >= 50) + { + dynAppend(errorList,navFunct_bareDBName(station)); + } + else if (val >= 40) + { + dynAppend(suspiciousList,navFunct_bareDBName(station)); + } + else if (val >= 30) + { + dynAppend(testList,navFunct_bareDBName(station)); + } + else if (val >= 20) + { + dynAppend(maintenanceList,navFunct_bareDBName(station)); + } + } if (strpos(dps[i],\"status.childState\") >= 0 && values[i] > highestChildState) highestChildState=values[i]; + + } + dynSort(offlineList); + dynSort(errorList); + dynSort(suspiciousList); + dynSort(testList); + dynSort(maintenanceList); + dynUnique(offlineList); + dynUnique(errorList); + dynUnique(suspiciousList); + dynUnique(testList); + dynUnique(maintenanceList); + + string toolTipText = \"Unavailable (but needed)<br> or errorstate stations in current observation(s) :<br>\"; + + for (int i = 1; i <= dynlen(maintenanceList);i++) + { + if (i == 1) toolTipText += \"In Maintenance mode: <br>\"; + toolTipText += maintenanceList[i] + \"<br>\"; + } + for (int i = 1; i <= dynlen(testList);i++) + { + if (i == 1) toolTipText += \"In Test mode: <br>\"; + toolTipText += testList[i] + \"<br>\"; + } + for (int i = 1; i <= dynlen(suspiciousList);i++) + { + if (i == 1) toolTipText += \"Suspicious: <br>\"; + toolTipText += suspiciousList[i] + \"<br>\"; } + for (int i = 1; i <= dynlen(errorList);i++) + { + if (i == 1) toolTipText += \"Error: <br>\"; + toolTipText += errorList[i] + \"<br>\"; + } + for (int i = 1; i <= dynlen(offlineList);i++) + { + if (i == 1) toolTipText += \"Stations offline: <br>\"; + toolTipText += offlineList[i] + \"<br>\"; + } + + setValue(\"childStateBorder\",\"toolTipText\",toolTipText); + setStates(highestState,highestChildState); } diff --git a/MAC/Navigator2/panels/objects/Hardware/powerUnit_small.pnl b/MAC/Navigator2/panels/objects/Hardware/powerUnit_small.pnl new file mode 100644 index 0000000000000000000000000000000000000000..a47224eeac69ad122e668d25a8932468da0ff1ff --- /dev/null +++ b/MAC/Navigator2/panels/objects/Hardware/powerUnit_small.pnl @@ -0,0 +1,296 @@ +V 11 +1 +LANG:1 0 +PANEL,-1 -1 388 166 N "_3DFace" 1 +"$station" +"main() +{ + dyn_dyn_anytype tab; + + station = $station+\":\"; + baseDP=station+\"LOFAR_PIC_POWEC0\"; + + if (dpExists(station+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\")) + { + dpGet(station+\"LOFAR_PIC_StationInfo.nrOfPowerUnits\",nrOfPowerUnits); + reload(); + } + else + { + setValue(\"powerUnit\", \"backCol\", \"Lofar_dpdoesnotexist\"); + } +} + +private void reload() +{ + + // check if the required datapoint for this view are enabled and accessible + if (navFunct_dpReachable(station+\"LOFAR_PIC_POWEC0\")) + { + dyn_string connectpoints; + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC0.nrOfModules:_online.._invalid\"); + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC0.nrOfModules:_online.._value\"); + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC0.OK:_online.._value\"); + if (nrOfPowerUnits == 2) + { + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC1.nrOfModules:_online.._invalid\"); + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC1.nrOfModules:_online.._value\"); + dynAppend(connectpoints,station+\"LOFAR_PIC_POWEC1.OK:_online.._value\"); + dpConnect(\"update2Powecs\",connectpoints); + } + else + { + dpConnect(\"updatePowec\",connectpoints); + } + } + else + { + if (dpExists(\"LOFAR_PIC_POWEC0\")) + { + setValue(\"powerUnit\", \"backCol\", \"Lofar_dpOffline\"); + } + else + { + setValue(\"powerUnit\", \"backCol\", \"Lofar_dpdoesnotexist\"); + } + } +} + +void updatePowec(string dp1, bool invalid, + string dp2, int nrOfModules, + string dp3, dyn_int OK) +{ + + string tooltip= station + \" PowerUnit <br>\"; + string color = \"Lofar_operational\"; + + if (invalid) + { + color = \"Lofar_invalid\"; + tooltip += \"POWEC0: invalid, unit might be turned off?<br> Or wrong snmp protocol\"; + } + else + { + + // Sometimes the boards doesn't give an answer anymore, in that case nrOfModules = 0, so thats an error, + // For now only a powercycle is known to resolve this issue + if (nrOfModules < 1) { + tooltip += \"POWEC0: nrOfModules = 0, unit might be stalled\"; + color = \"Lofar_suspicious\"; + } + else if (nrOfModules != dynlen(OK)) + { + tooltip += \"POWEC0: nrOfModules and OKArray length differ\"; + color = \"Lofar_suspicious\"; + } + else + { + tooltip += \"POWEC0: <br>\"; + bool ok = true; + for (int i = 1; i <= nrOfModules; i++) + { + if (OK[i] != 1) + { + ok = false; + tooltip += \" Module [\"+i+\"] not OK <br>\"; + color = \"Lofar_broken\"; + } + } + if (ok) tooltip += \"All Modules OK\"; + } + } + setValue(\"powerUnit\",\"toolTipText\",tooltip); + setValue(\"powerUnit\", \"backCol\", color); +} + +void update2Powecs(string dp1, bool invalid1, + string dp2, int nrOfModules1, + string dp3, dyn_int OK1, + string dp4, bool invalid2, + string dp5, int nrOfModules2, + string dp6, dyn_int OK2) +{ + string tooltip= station + \" PowerUnit <br>\"; + string color = \"Lofar_operational\"; + + if (invalid1) + { + color = \"Lofar_invalid\"; + tooltip += \"POWEC0: invalid, unit might be turned off?<br> Or wrong snmp protocol\"; + } + else if (invalid2) + { + color = \"Lofar_invalid\"; + tooltip += \"POWEC1: invalid, unit might be turned off?<br> Or wrong snmp protocol\"; + } + // Sometimes the boards doesn't give an answer anymore, in that case nrOfModules = 0, so thats an error, + // For now only a powercycle is known to resolve this issue + else if (nrOfModules1 < 1) + { + tooltip += \"POWEC0: nrOfModules = 0, unit might be stalled\"; + color = \"Lofar_suspicious\"; + } + else if (nrOfModules2 < 1) + { + tooltip += \"POWEC1: nrOfModules = 0, unit might be stalled\"; + color = \"Lofar_suspicious\"; + } + else if (nrOfModules1 != dynlen(OK1)) + { + tooltip += \"POWEC0: nrOfModules and OKArray length differ\"; + color = \"Lofar_suspicious\"; + } + else if (nrOfModules2 != dynlen(OK2)) + { + tooltip += \"POWEC1: nrOfModules and OKArray length differ\"; + color = \"Lofar_suspicious\"; + } + else + { + tooltip += \"POWEC0: </br>\"; + bool ok = true; + for (int i = 1; i <= nrOfModules1; i++) + { + if (OK1[i] != 1) + { + ok = false; + tooltip += \" Module [\"+i+\"] not OK </br>\"; + color = \"Lofar_broken\"; + } + } + if (ok) tooltip += \"All Modules OK <br>\"; + + ok = true; + tooltip += \"POWEC1: </br>\"; + + for (int i = 1; i <= nrOfModules2; i++) + { + if (OK2[i] != 1) + { + ok = false; + tooltip += \" Module [\"+i+\"] not OK </br>\"; + color = \"Lofar_broken\"; + } + } + if (ok) tooltip += \"All Modules OK <br>\"; + } + + setValue(\"powerUnit\",\"toolTipText\",tooltip); + setValue(\"powerUnit\", \"backCol\", color); + +} + +" 0 + E "main(int x, int y) +{ + click(); +}" 0 + "main() +{ + rClick(); +}" 0 + "main(int x, int y) +{ + dblClick(); +}" 0 + 1 -1 -1 0 0 0 +""0 1 +E "#uses \"navPanel.ctl\" +string station = \"\"; +string baseDP=\"\"; +int nrOfPowerUnits = 0; + +bool bDoubleClicked = false; + +// routine for single mouse click +void click() { + // set delay in case double click was meant + delay(0, 100); + if (!bDoubleClicked) { + navPanel_setEvent(station,\"EventClick\"); + } +} + +// routine for double mouse click +void dblClick() { + // indicate this is a doubleClick + bDoubleClicked = true; + + if (dpExists(baseDP) ) { + LOG_DEBUG(\"powerUnit_small.pnl:DoubleClick|Setting currentDatapoint from : \"+g_currentDatapoint+\" to \"+baseDP); + g_currentDatapoint=baseDP; + navPanel_setEvent(station,\"ChangePanel\"); + } + + // set delay to avoid click event will be triggered + delay(0, 500); + bDoubleClicked = false; +} + +// routine for right mouse click +void rClick() { +}" 0 + 2 +"CBRef" "1" +"EClose" E +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 0 +6 0 +"powerUnit" +"" +1 230 150 E E E 1 E 1 E N {0,0,0} E N {255,255,255} E E + "main() +{ + rClick(); +}" 0 + "main() +{ + dblClick(); +}" 0 + +0 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +1 +"dashclr"N "_Transparent" +E "main() +{ + click(); +}" 0 + 0 1 1 2 1 E 1 0 1 0 -10 1 E 0 10 14 19 +0 +LAYER, 1 +1 +LANG:1 0 +0 +LAYER, 2 +1 +LANG:1 0 +0 +LAYER, 3 +1 +LANG:1 0 +0 +LAYER, 4 +1 +LANG:1 0 +0 +LAYER, 5 +1 +LANG:1 0 +0 +LAYER, 6 +1 +LANG:1 0 +0 +LAYER, 7 +1 +LANG:1 0 +0 +0 diff --git a/MAC/Navigator2/panels/objects/Observations/Observation_small.pnl b/MAC/Navigator2/panels/objects/Observations/Observation_small.pnl index bcdaf972b36b31a8f0316650d27f522976a53e15..b1a8fac91c374f54a90a7899c7877c2f8c25f49f 100644 --- a/MAC/Navigator2/panels/objects/Observations/Observation_small.pnl +++ b/MAC/Navigator2/panels/objects/Observations/Observation_small.pnl @@ -110,10 +110,7 @@ updateObservations(string dp1, dyn_string obs, if (!navFunct_dpReachable(obsDP) ){ LOG_ERROR(\"Observation_small.pnl:updateObservations|DP unreachable\"); updateObservationsTableValues(\"\",0,\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",true); - } - - } else if (!dpExists(obsDP)){ - LOG_ERROR(\"Observation_small.pnl:updateObservations|ERROR: Dp for LOFAR_ObsSW_\"+obs[i]+\" doesn't exist.\"); + } } } } diff --git a/MAC/Navigator2/panels/objects/Observations/Pipeline_small.pnl b/MAC/Navigator2/panels/objects/Observations/Pipeline_small.pnl index 5da3b1cd5f3377d49368910fbf25aff0fbff2650..588dd15d8e0425fd14e1a7082cc7542afd04a5d4 100644 --- a/MAC/Navigator2/panels/objects/Observations/Pipeline_small.pnl +++ b/MAC/Navigator2/panels/objects/Observations/Pipeline_small.pnl @@ -107,10 +107,7 @@ updatePipelines(string dp1, dyn_string obs, if (!navFunct_dpReachable(obsDP) ){ LOG_ERROR(\"Pipeline_small.pnl:updatePipelines|DP unreachable\"); updatePipelinesTableValues(\"\",0,\"\",\"\",\"\",\"\",\"\",\"\",\"\",true); - } - - } else if( !dpExists(obsDP) ) { - LOG_ERROR(\"Pipeline_small.pnl:updatePipelines|ERROR: Dp for LOFAR_ObsSW_\"+obs[i]+\" doesn't exist.\"); + } } } } diff --git a/MAC/Navigator2/panels/objects/Processes/SWControlerTempObs_small.pnl b/MAC/Navigator2/panels/objects/Processes/SWControlerTempObs_small.pnl index 6f39ca7a1d20efb5d3b304a8b09de4277dc076fa..a6c5a9acccfaf07b75bfe10cbe59ae1005bb382c 100644 --- a/MAC/Navigator2/panels/objects/Processes/SWControlerTempObs_small.pnl +++ b/MAC/Navigator2/panels/objects/Processes/SWControlerTempObs_small.pnl @@ -11,28 +11,36 @@ PANEL,-1 -1 399 95 N "_3DFace" 2 baseDP = station+\"LOFAR_\"+$name; setValue(\"process\", \"toolTipText\", baseDP); - string lvlDP = station+\"LOFAR_PermSW_Daemons_SoftwareMonitor\"; // Connect to SWLevel - if (navFunct_dpReachable(lvlDP)) { - if(dpExists(lvlDP+\".SWLevel\")) { + if (navFunct_dpReachable(lvlDP)) + { + if(dpExists(lvlDP+\".SWLevel\")) + { if (dpConnect(\"callbackLevelChanged\", lvlDP+\".SWLevel:_online.._value\", - lvlDP+\".SWLevel:_online.._invalid\") == -1) { + lvlDP+\".SWLevel:_online.._invalid\") == -1) + { LOG_ERROR(\"SWControlerTempObs_small.pnl:main|Couldn't connect to: \"+lvlDP+\".SWLevel\"); } - } else { + } + else + { LOG_ERROR(\"SWControlerTempObs_small.pnl:main|\"+lvlDP+\".SWLevel not found\"); } } // Connect to active observations - if (navFunct_dpReachable(MainDBName+\"LOFAR_PermSW_MACScheduler.activeObservations\")) { - if (dpConnect(\"callbackObservationChanged\", MainDBName+\"LOFAR_PermSW_MACScheduler.activeObservations:_online.._value\") == -1) { + if (navFunct_dpReachable(MainDBName+\"LOFAR_PermSW_MACScheduler.activeObservations\")) + { + if (dpConnect(\"callbackObservationChanged\", MainDBName+\"LOFAR_PermSW_MACScheduler.activeObservations:_online.._value\") == -1) + { LOG_ERROR(\"SWControlerTempObs_small.pnl:main|Couldn't connect to: \"+MainDBName+\":LOFAR_PermSW_MACScheduler.activeObservations: \"+getLastError()); } - } else { + } + else + { if (!isStandalone()) LOG_ERROR(\"SWControlerTempObs_small.pnl:main|\"+MainDBName+\" seems offline\"); } @@ -43,28 +51,26 @@ void callbackLevelChanged(string dp1, int swlvlCB, string dp2, bool swinvalidCB) { // Store in scope var + if (swlvl == swlvlCB) { + return; + } + swlvl = swlvlCB; swinvalid = swinvalidCB; + checkStateConnection(); } - - - - - private void callbackObservationChanged(string dp1, dyn_string observationsCB) { // Store in scope var observations = observationsCB; + checkStateConnection(); } - - - private void checkStateConnection() { @@ -72,22 +78,28 @@ private void checkStateConnection() string firstPipeline = \"\"; // for now the object will only show the controller for the first observation // We also have to check if the swleve;l is higher then 6 for the obs dependant ctrl'ers - if (dynlen(observations) >= 1) { - for (int i = 1; i <= dynlen(observations); i++) { - if ($name != \"PythonControl\" && navFunct_isObservation(observations[i]) && firstObservation == \"\") { + if (dynlen(observations) >= 1) + { + for (int i = 1; i <= dynlen(observations); i++) + { + if ($name != \"PythonControl\" && navFunct_isObservation(observations[i]) && firstObservation == \"\") + { firstObservation = observations[i]; - } else if ($name == \"PythonControl\" && !navFunct_isObservation(observations[i]) && firstPipeline == \"\") { + } + else if ($name == \"PythonControl\" && !navFunct_isObservation(observations[i]) && firstPipeline == \"\") + { firstPipeline = observations[i]; } } - - if ($name != \"PythonControl\" && firstObservation == \"\") { + if ($name != \"PythonControl\" && firstObservation == \"\") + { setValue(\"process\", \"backCol\", \"Lofar_off\"); return; } - if ($name == \"PythonControl\" && firstPipeline == \"\") { + if ($name == \"PythonControl\" && firstPipeline == \"\") + { setValue(\"process\", \"backCol\", \"Lofar_off\"); return; } @@ -114,10 +126,12 @@ private void checkStateConnection() // Disconnect if( (baseDPconnected != \"\") && (baseDP != baseDPconnected) ) { + if (dpDisconnect(\"updateSWController\", baseDPconnected +\".status.state:_online.._value\", - baseDPconnected +\".status.state:_online.._invalid\") == -1) { - baseDPconnected = \"\"; - } + baseDPconnected +\".status.state:_online.._invalid\") != -1) + { + setValue(\"process\", \"backCol\", \"Lofar_off\"); + } } @@ -125,31 +139,43 @@ private void checkStateConnection() if( (baseDP != \"\") && (baseDP != baseDPconnected) ) { - if (navFunct_dpReachable(baseDP+\".status.state\")) { - if (dpExists(baseDP+\".status.state\")) { + if (navFunct_dpReachable(baseDP+\".status.state\")) + { + if (dpExists(baseDP+\".status.state\")) + { if (dpConnect(\"updateSWController\", baseDP +\".status.state:_online.._value\", - baseDP +\".status.state:_online.._invalid\") == -1) { + baseDP +\".status.state:_online.._invalid\") == -1) + { setValue(\"process\", \"backCol\", \"Lofar_dpdoesnotexist\"); LOG_ERROR(\"SWControlerTempObs_small.pnl:reload|Couldn't connect updateSWController: \"+getLastError()); - } else { + } + else + { baseDPconnected = baseDP; } - } else { - setValue(\"process\", \"backCol\", \"Lofar_dpdoesnotexist\"); + } + else + { + setValue(\"process\", \"backCol\", \"Lofar_dpdoesnotexist\"); } - } else { + } + else + { setValue(\"process\", \"backCol\", \"Lofar_dpOffline\"); } } - if ($name == \"PythonControl\") + if ($name == \"PythonControl\" && firstPipeline != \"\" && swlvl == 6) { - setValue(\"process\", \"backCol\", \"Lofar_off\"); + setValue(\"process\", \"backCol\", \"Lofar_operational\"); } - + else if ($name == \"PythonControl\") + { + setValue(\"process\", \"backCol\", \"Lofar_off\"); + } } @@ -159,7 +185,12 @@ private void checkStateConnection() updateSWController(string dp1, int status, string dp2, bool invalid) { - + if (!navFunct_dpReachable(station)) + { + setValue(\"process\", \"backCol\", \"Lofar_dpOffline\"); + return; + } + if (invalid) { setValue(\"process\", \"backCol\", \"Lofar_invalid\"); @@ -168,8 +199,7 @@ updateSWController(string dp1, int status, else { setValue(\"process\", \"backCol\", getStateColor(status)); - } - + } } " 0 @@ -218,7 +248,7 @@ void dblClick() { bDoubleClicked = true; if (dpExists(baseDP) ) { - LOG_DEBUG(\"SWcontroller_small.pnl:DoubleClick|Setting currentDatapoint from : \"+g_currentDatapoint+\" to \"+baseDP); + LOG_DEBUG(\"SWcontrollerTempObs_small.pnl:DoubleClick|Setting currentDatapoint from : \"+g_currentDatapoint+\" to \"+baseDP); g_currentDatapoint=baseDP; //we also have to set the tabctrl to think this was initiated from the ProcessesTab, otherwise we will get the wrong panel. navPanel_setEvent(\"Processes\",\"ChangeTab\"); diff --git a/MAC/Navigator2/panels/objects/Processes/observationFlow_cobaltNodeProcesses.pnl b/MAC/Navigator2/panels/objects/Processes/observationFlow_cobaltNodeProcesses.pnl index 5f50303641cdbb5a8856fd5c83bdffd7d0554ce8..6620462b79206abe74e0c2363685c51f6403d01f 100644 --- a/MAC/Navigator2/panels/objects/Processes/observationFlow_cobaltNodeProcesses.pnl +++ b/MAC/Navigator2/panels/objects/Processes/observationFlow_cobaltNodeProcesses.pnl @@ -169,9 +169,6 @@ void connectCobaltNodesAndProcesses(string runState) { dpConnect(\"cobaltNodesAndProcessesCB\",TRUE,stateDPs); dyn_errClass err = getLastError(); //test whether an error occurred if(dynlen(err) > 0) { - errorDialog(err); - // open dialog box with errors - throwError(err); // write errors to stderr LOG_ERROR(\"ObservationFlow_cobaltNodeProcesses.pnl:connectCobaltNodesAndProcesses| ERROR: connect fails:\"+ stateDPs); } else { connectedStates = true; diff --git a/MAC/Navigator2/panels/objects/baseLine.pnl b/MAC/Navigator2/panels/objects/baseLine.pnl index 6167e5c119a1431a57008076335fd7b4c1618835..ad48ada04cbd1e3cbc8232a2865e08faec20b2ae 100644 --- a/MAC/Navigator2/panels/objects/baseLine.pnl +++ b/MAC/Navigator2/panels/objects/baseLine.pnl @@ -190,7 +190,7 @@ void swlevel_stationCallback(string dp1, bool aTrig) { { click(); }" 0 - 0 1 1 2 1 E U 1 E 0 0 931 15 + 0 1 1 2 1 E U 1 E 0 0 961 15 6 0 "bar" "" @@ -204,11 +204,11 @@ LANG:1 0 1 "dashclr"N "_Transparent" -E E 0 0 1 2 1 E U 1 E 3 3 928 12 +E E 0 0 1 2 1 E U 1 E 3 3 958 12 2 4 "station_text" "" -1 890 0 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E +1 928 0 E E E 1 E 1 E N "_WindowText" E N "_Transparent" E E "main() { rClick(station); @@ -230,8 +230,8 @@ E "main(int x, int y) { click(station); }" 0 - 0 1 1 2 1 E U 0 E 890 0 918 13 -0 2 2 "0s" 0 0 0 192 0 0 890 0 1 + 0 1 1 2 1 E U 0 E 928 0 956 13 +0 2 2 "0s" 0 0 0 192 0 0 928 0 1 1 LANG:1 34 MS Shell Dlg 2,8,-1,5,50,0,0,0,0,0 0 1 diff --git a/MAC/Navigator2/panels/objects/genericOnOffView.pnl b/MAC/Navigator2/panels/objects/genericOnOffView.pnl index 01fb943a6ba090d1ab0d31c93985eea3371785ac..210c9e2240311d160de94b4a47bf06535dbf2704 100644 --- a/MAC/Navigator2/panels/objects/genericOnOffView.pnl +++ b/MAC/Navigator2/panels/objects/genericOnOffView.pnl @@ -18,7 +18,6 @@ private void reload() { if (navFunct_dpReachable(fullDP)) { string dp = dpSubStr(fullDP,DPSUB_DP); - setValue(\"viewObject\",\"toolTipText\",fullDP ); if ( dp != \"\") { baseDP = station+dp; @@ -47,7 +46,8 @@ updateViewObject(string dp1, bool on, string color = \"Lofar_operational\"; if (!on) { color = \"Lofar_broken\"; - } + } + setValue(\"viewObject\",\"toolTipText\",\"Power: \" + on ); setValue(\"viewObject\",\"backCol\", color); }" 0 E E E E 1 -1 -1 0 0 0 diff --git a/MAC/Navigator2/panels/objects/navigator_viewSelection.pnl b/MAC/Navigator2/panels/objects/navigator_viewSelection.pnl index ecd62b860c5e5be4a12a3fc7955bc4dd2ec9eb96..7c53e2c416408917a7d7721f0df35ee18417bdc1 100644 --- a/MAC/Navigator2/panels/objects/navigator_viewSelection.pnl +++ b/MAC/Navigator2/panels/objects/navigator_viewSelection.pnl @@ -139,7 +139,7 @@ LANG:1 6 Alerts 22 14 "panelChoice" "" -1 1043 36 E E E 1 E 0 E N "_WindowText" E N "_Window" E E +1 1060 36 E E E 1 E 0 E N "_WindowText" E N "_Window" E E E E 15 0 0 0 0 0 E E E @@ -150,7 +150,7 @@ LANG:1 0 0 1 LANG:1 33 MS Shell Dlg,-1,11,5,50,0,0,0,0,0 -0 1041 34 1195 55 +0 1058 34 1195 55 0 E @@ -220,7 +220,7 @@ LANG:1 5 email if (id == \"3\") { ModuleOnWithPanel(\"LOFAR logger settings\", -1, -1, 0, 0, 1, 1,\"\", \"objects/lofar_logger.pnl\", \"\" , makeDynString()); } else if (id == \"4\") { - ModuleOnWithPanel(\"Test Panel\", -1, -1, 0, 0, 1, 1, \"\", \"Test/Event_Viewer.pnl\", \"\" , makeDynString()); + ModuleOnWithPanel(\"Test Panel\", -1, -1, 0, 0, 1, 1, \"\", \"Test/Navigator_testPanel.pnl\", \"\" , makeDynString()); } else if (id == \"5\") { ModuleOnWithPanel(\"Email settings\", -1, -1, 0, 0, 1, 1, \"\", \"Settings/mail.pnl\", \"\" , makeDynString()); } diff --git a/MAC/Navigator2/panels/vision/aes/AES_properties.pnl b/MAC/Navigator2/panels/vision/aes/AES_properties.pnl new file mode 100644 index 0000000000000000000000000000000000000000..6e491155d318ba217d3750a46cd58e31504cf9a7 --- /dev/null +++ b/MAC/Navigator2/panels/vision/aes/AES_properties.pnl @@ -0,0 +1,648 @@ +V 11 +2 +LANG:1 25 Properties of alert panel +LANG:0 29 Eigenschaften von Meldeschirm +PANEL,-1 -1 480 554 N "_3DFace" 0 +"//#uses \"AES_peter.ctl\" + +main() +{ + dyn_string configNames; + + // we need this call to build new panelglobal vstn/ddares object +// aes_reload(); + + // saving all important values to panelglobal variables + g_propDpName =getDollarValue( AESREGDOLLAR_PROPDP ); + g_balPropDpName=getDollarValue( AESREGDOLLAR_BALPROPDP ); + g_configName =getDollarValue( AESREGDOLLAR_CONFIGNAME ); + g_alertRow =getDollarValue( AESREGDOLLAR_ALERTROW ); + g_configPanel =getDollarValue( AESREGDOLLAR_CONFIGPANEL ); + + if( isDollarDefined( AESREGDOLLAR_COLTITLES ) ) + { + g_colTitles=aec_s2ds( getDollarValue( AESREGDOLLAR_COLTITLES ), AEC_SEP ); + } + + if( isDollarDefined( AESREGDOLLAR_COLNAMES ) ) + { + g_colNames=aec_s2ds( getDollarValue( AESREGDOLLAR_COLNAMES ), AEC_SEP ); + } + +//DebugN(\"Properties titles(\" + dynlen(g_colTitles) + \")=\" + g_colTitles ); +//DebugN(\"Properties names(\" + dynlen(g_colNames) + \")=\" + g_colNames ); + + aes_getScreenType( g_propDpName, g_screenType ); + aes_getTabType( g_propDpName, g_tabType ); + + if( !aes_operationPermission( g_configName, AES_OPERTYPE_PROPERTIES, AES_OPER_REMOVE ) ) + { + //��� dialog + setMultiValue( \"pb_saveConfig\", \"enabled\", false, + \"pb_deleteConfig\", \"enabled\", false ); + } + else + { + int deletePermission; //contains the permission to delete a DP + dpGet(\"_System.Auth.Dp:_original.._value\", deletePermission); // Querie the userpermission which is needed to delete a DP + setMultiValue( \"pb_saveConfig\", \"enabled\", true, + \"pb_deleteConfig\", \"enabled\", getUserPermission(deletePermission)); // set enabled if user is allowed to delete a DP + } + + // if we were in config panel, we have to copy property settings to dummy runtime dp + // even screenType, tabType + if( g_configPanel ) + { + //aes_copyDp(); + } + + aes_propInit( g_screenType, g_tabType, g_configName, g_propDpName ); + + if( g_alertRow ) + { + ti_type.enabled=false; + } + + setValue( \"sl_tmpVisCol\", \"items\", g_colTitles ); + + reg_main.visible=true; +}" 0 + E E E E 1 -1 -1 0 13 8 +""0 1 +E "//////// neu ******* begin +// neu +dyn_dyn_anytype vstn; +dyn_anytype ddaRes; + +// neu - jetzt �ber dollar var +dyn_string g_colNames; +dyn_string g_colTitles; + +// the following variables will be set in initial section +string g_propDpName; +string g_balPropDpName; // balanced propDpName +string g_configName; +int g_screenType; +int g_tabType; +bool g_alertRow; +bool g_configPanel; + +const int +COLIDX=1, +PANEL=2, +COLNAME=3, +COLVIS=4, +COLSCRRENTYPE=5, +COLTABTYPE=6, +COLCONFIGNAME=7; + +int +firstAlertReg, lastAlertReg, +firstEventReg, lastEventReg; + +// const +int +//alerts +PREGA_TIMERANGE, +PREGA_FILTER, +PREGA_FILTERTYPES, +PREGA_FILTERSTATE, +PREGA_FILETERSYSTEM, +PREGA_SORT, +PREGA_VISIBLE, +PREGA_GENERAL, +// events +PREGE_TIMERANGE, +PREGE_FILTER, +PREGE_FILTERTYPES, +PREGE_FILTERSTATE, +PREGE_FILETERSYSTEM, +PREGE_SORT, +PREGE_VISIBLE, +PREGE_GENERAL; + +closePanel() +{ + string config; + dyn_string ds, configList; + dyn_float df; + int pos; + + configList=configName.items; + config=configName.text; + + if( dynlen( configList ) > 0 ) + { + ds=configList; + pos=dynContains( configList, config ); + if( pos > 0 ) + { + df[1]=pos; + } + else + { + df[1]=0; + } + } + else + { + df[1]=0; + ds=makeDynString(); + } + + df[2]=AES_CONF_CANCEL; + + // we don't write back data to runtime dp at cancel + PanelOffReturn( df, ds ); +}" 0 + 2 +"CBRef" "0" +"EClose" "main() +{ + closePanel(); +}" 0 + +"" +DISPLAY_LAYER, 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 +LAYER, 0 +1 +LANG:1 6 Layer1 +30 18 +"Frame1" +"" +1 10 499 E E E 1 E 1 E N "_3DText" E N "_Transparent" E E + E E +18 0 0 0 0 0 +E E E +1 +2 +LANG:1 0 +LANG:0 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 0 1 E 1 0 0.9322033898305084 0 29.83050847457629 0 E 10 440 470 500 +2 +LANG:1 26 Arial,10,-1,5,75,0,0,0,0,0 +LANG:0 26 Arial,10,-1,5,75,0,0,0,0,0 +0 2 +LANG:1 10 Properties +LANG:0 13 Eigenschaften +2 20 +"Text1" +"" +1 110 510 E E E 1 E 0 E N {0,0,0} E N "_Transparent" E E + E E +19 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +1 +"dashclr"N "_Transparent" +E E 0 1 1 0 1 E U 0 E 112 512 189 528 +0 2 0 "0s" 0 0 0 192 0 0 112 512 1 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 2 +LANG:1 14 Active config: +LANG:0 14 Aktive Konfig: +13 14 +"Button4" +"" +1 340 459 E E E 0 E 0 E N "_ButtonText" E N "_Button" E E + E E +14 0 0 0 0 0 +E E E +0 +2 +LANG:1 11 Open config +LANG:0 13 Konfig �ffnen + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 338 457 366 485 + +P +10862530 +"pictures/StandardIcons/Open_20.png" +2 +LANG:1 0 +LANG:0 0 +"main() +{ + aes_loadPropertyConfig(); +}" 0 + E E E +22 5 +"configName" +"" +1 17.84375 460 E E E 1 E 1 E N {0,0,0} E N "_Window" E E + E E +2 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 18 458 367 484 +0 + +E +"main() +{ + aes_loadPropertyConfig(); +}" 0 + +E + 0 0 +13 1 +"ok" +"" +1 285 519 E E E 1 E 1 E N {0,0,0} E N "_Button" E E + E E +6 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 283 518 371 546 + +T +2 +LANG:1 2 OK +LANG:0 2 OK +"main() +{ + + int screenType, tabType; + string config; + dyn_string ds, configList; + dyn_float df; + int pos, ret; + + configList=configName.items; + config=configName.text; + + // only this set operation write datas to runtime dp + aes_setProps( g_propDpName, config, g_screenType, g_tabType, ret, false, false); + + // saving configchange to runtime dp + dpSetWait( g_propDpName + \".Settings.Config\" + AES_ORIVAL, config ); + + if( dynlen( configList ) > 0 ) + { + ds=configList; + pos=dynContains( configList, config ); + + if( pos > 0 ) + { + df[1]=pos; + } + else + { + df[1]=0; + } + } + else + { + df[1]=0; + ds=makeDynString(); + } + + df[2]=AES_CONF_OK; + + PanelOffReturn( df, ds ); +}" 0 + E E E +13 2 +"abbrechen" +"" +1 375 519 E E E 1 E 1 E N {0,0,0} E N "_Button" E E + E E +7 0 0 0 27 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 373 518 461 546 + +T +2 +LANG:1 6 Cancel +LANG:0 9 Abbrechen +"main() +{ + closePanel(); +}" 0 + E E E +28 11 +"reg_main" +"" +1 70 85 E E E 1 E 0 E N "_3DText" E N "_3DFace" E E + E E +12 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,12,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,12,5,40,0,0,0,0,0 +0 8 8 472 432 +E8 "#0" 2 +LANG:1 2 #A +LANG:0 2 #A +0 +"#1" 2 +LANG:1 2 #B +LANG:0 2 #C +0 +"#2" 2 +LANG:1 2 #C +LANG:0 2 #D +0 +"#3" 2 +LANG:1 2 #D +LANG:0 2 #E +0 +"#4" 2 +LANG:1 2 #E +LANG:0 2 #F +0 +"#5" 2 +LANG:1 2 #F +LANG:0 2 #G +0 +"#6" 2 +LANG:1 2 #G +LANG:0 2 #H +0 +"#7" 2 +LANG:1 2 #H +LANG:0 2 #I +0 + +13 12 +"pb_saveConfig" +"" +1 405 459 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +12 0 0 0 0 0 +E E E +0 +2 +LANG:1 13 Save property +LANG:0 21 Eigenschaft speichern + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 403 457 431 485 + +P +13160660 +"pictures/StandardIcons/Save_20.png" +2 +LANG:1 0 +LANG:0 0 +"main() +{ + aes_savePropertyConfig(); +}" 0 + E E E +13 13 +"pb_deleteConfig" +"" +1 434 459 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +13 0 0 0 0 0 +E E E +0 +2 +LANG:1 15 Delete property +LANG:0 19 Eigenschaft l�schen + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 433 457 461 485 + +P +13160660 +"pictures/StandardIcons/delete_20.png" +2 +LANG:1 0 +LANG:0 0 +"main() +{ + aes_removePropertyConfig(); +}" 0 + E E E +13 15 +"Button1" +"" +1 20 510 E E E 1 E 0 E N "_ButtonText" E N "_Button" E E + E E +15 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 18 518 46 546 + +P +13434828 +"15.png" +2 +LANG:1 0 +LANG:0 0 +E E E E +13 16 +"Button2" +"" +1 80 510 E E E 1 E 0 E N "_ButtonText" E N "_Button" E E + E E +16 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 78 518 106 546 + +P +13434828 +"16.png" +2 +LANG:1 0 +LANG:0 0 +E E E E +13 17 +"Button3" +"" +1 50 510 E E E 1 E 0 E N "_ButtonText" E N "_Button" E E + E E +17 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 48 518 76 546 + +P +13434828 +"17.png" +2 +LANG:1 0 +LANG:0 0 +E E E E +13 19 +"pb_newConfig" +"" +1 374 459 E E E 1 E 1 E N "_ButtonText" E N "_Button" E E + E E +19 0 0 0 0 0 +E E E +0 +2 +LANG:1 12 New property +LANG:0 23 Eigenschaft neu anlegen + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 373 457 401 485 + +P +13160660 +"pictures/StandardIcons/new_20.png" +2 +LANG:1 0 +LANG:0 0 +"main() +{ + aes_addPropertyConfig(); +}" 0 + E E E +14 21 +"txtConfigName" +"" +1 150 520 E E E 1 E 0 E N "_WindowText" E N "_3DFace" E E + E E +20 0 0 0 0 0 +E E E +0 +2 +LANG:1 0 +LANG:0 0 + +0 +2 +LANG:1 26 Arial,-1,13,5,40,0,0,0,0,0 +LANG:0 26 Arial,-1,13,5,40,0,0,0,0,0 +0 148 518 252 544 +3 "0s" 0 0 0 0 0 -1 E E E +1 25 0 "" 0 +0 +1 26 0 "" 1 +0 +29 24 +"BackgroundCover_ewo2" +"" +1 370 455 E E E 1 E 1 E N "_3DText" E N {240,240,240} E E + E E +21 0 0 0 0 0 +E E E +0 +1 +LANG:1 0 + +0 +1 +LANG:1 37 MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0 +0 370 455 465 488 +19 BackgroundCover.ewo +0 +"main() +{ + this.visible = !isModeExtended(); +}" 0 +0 +LAYER, 1 +1 +LANG:1 6 Layer2 +0 +LAYER, 2 +1 +LANG:1 6 Layer3 +0 +LAYER, 3 +1 +LANG:1 6 Layer4 +0 +LAYER, 4 +1 +LANG:1 6 Layer5 +0 +LAYER, 5 +1 +LANG:1 6 Layer6 +0 +LAYER, 6 +1 +LANG:1 6 Layer7 +0 +LAYER, 7 +1 +LANG:1 6 Layer8 +0 +3 0 "PANEL_REF0" -1 +"objects_parts\\STD_OBJECTS\\ButtonBarBackground.pnl" 15 510 T 21 1.050100200400802 0 1 -25.75150300601199 0 +0 +0 diff --git a/MAC/Navigator2/scripts/claim.ctl b/MAC/Navigator2/scripts/claim.ctl index 3f7548a30583d7c53af6d422597bfb1607876d16..54702e94a795c6dca5b1fd8c72515fce766e6fa0 100644 --- a/MAC/Navigator2/scripts/claim.ctl +++ b/MAC/Navigator2/scripts/claim.ctl @@ -386,7 +386,7 @@ void clientAddClaimCallback( // Description: // Here the claiming mechanism will start // Obvious it's the same for master as well as client, -// since they bove have their own internal mapping. +// since they both have their own internal mapping. // // Returns: // None @@ -979,11 +979,6 @@ void checkAndCreateDPs() { dpCreate("LOFAR_ObsSW_TempObs"+pre+"_OnlineControl","OnlineControl"); changed = true; } - //PythonControl - if (!dpExists("LOFAR_ObsSW_TempObs"+pre+"_PythonControl")) { - dpCreate("LOFAR_ObsSW_TempObs"+pre+"_PythonControl","PythonControl"); - changed = true; - } //CobaltGPUProc for (int k=1; k < 10; k++) { for (int l=0; l < 2; l++) { diff --git a/MAC/Navigator2/scripts/libs/claimManager.ctl b/MAC/Navigator2/scripts/libs/claimManager.ctl index b8583737fef8a2690ea8d05fe2c4b71d4cef51d4..458e83354133da4255341d1a51caf7413acfd189 100644 --- a/MAC/Navigator2/scripts/libs/claimManager.ctl +++ b/MAC/Navigator2/scripts/libs/claimManager.ctl @@ -35,8 +35,11 @@ // the name of the datapoints plus the actual name // This is used to quickly determine the name of the real datapoint // from the graphical user interface -global dyn_string strClaimDPName; // datapoint that was claimed -global dyn_string strClaimObjectName; // Actual object name +global dyn_string strClaimDPName; // datapoint that was claimed +global dyn_string strClaimObjectName; // Actual object name +global int g_freeClaims = 0; // keeps track of free claims +global int g_usedClaims = 0; // keeps track of used claims +global int g_unusedClaims = 0; // keeps track of unused claims // **************************************** @@ -117,93 +120,88 @@ string claimManager_realNameToName( string strName ) void claimManager_queryConnectClaims() { - // Local data - string DPT = "Observation"; - if (g_standAlone) { - DPT = "StnObservation"; - } - string strQuery = "SELECT '.claim.name:_original.._value, .claim.claimDate:_original.._value, .claim.freeDate:_original.._value' FROM 'LOFAR_ObsSW_*' WHERE _DPT = \""+DPT+"\""; - - LOG_DEBUG( "claimManager.ctl:claimManager_queryConnectClaims|*** Doing a query for : claimManager_QueryConnectClaims() " ); - - // Trigger a single query that gets an update when one - // claim changes - if (dpQueryConnectSingle( "claimManager_queryConnectClaim_Callback", 1, "ident_claim", strQuery ) == -1) { - LOG_ERROR( "claimManager.ctl:claimManager_queryConnectClaims|dpQueryConnectSingle failed" ); - if ( g_initializing ) { + string baseDP = MainDBName+"ClaimManager.cache"; + if (dpExists(baseDP)) { + if (dpConnect("claimManager_updateCacheClaims",true, baseDP + ".newObjectNames:_online.._value", + baseDP + ".DPNames:_online.._value", + baseDP + ".claimDates:_online.._value", + baseDP + ".freeDates:_online.._value", + baseDP + ".newObjectNames:_online.._invalid") == -1) + { + LOG_DEBUG("Claim_Viewer.pnl:main|Couldn't connect to: "+baseDP); + } + else + { + LOG_DEBUG("Claim_Viewer.pnl:main|Connected to: " + baseDP); + } + } + else + { + if (!isStandalone()) { + LOG_ERROR( "claimManager.ctl:claimManager_queryConnectClaims|Couldn't find DP to connect to: "+baseDP); + } + if ( g_initializing ) + { writeInitProcess("connectClaimsFinished"); } } } -void claimManager_queryConnectClaim_Callback(string strIdent, dyn_dyn_anytype aResult) +claimManager_updateCacheClaims(string dp1, dyn_string objectNames, + string dp2, dyn_string DPNames, + string dp3, dyn_time claimDates, + string dp4, dyn_time freeDates, + string dp5, bool invalid) { - // Local data - int iPos; - string aDP; - string strDP; - string strName; - time tClaimDate; - time tFreeDate; - bool bClaimed; + dyn_string claimedObjectNames; + dyn_string claimedDPNames; - LOG_DEBUG( "claimManager.ctl:claimManager_queryConnectClaim_Callback| has " + dynlen( aResult ) + " results" ); - LOG_DEBUG( "claimManager.ctl:claimManager_queryConnectClaim_Callback| "+aResult); - if( dynlen( aResult ) < 2 ) { - writeInitProcess("connectClaimsFinished"); - return; + if (invalid) + { + LOG_WARN("claimManager.ctl:claimManager_updateCacheClaims|ClaimManager.cache is invalid"); + if ( g_initializing ) { + writeInitProcess("connectClaimsFinished"); } + return; + } + + int unused = 0; + int claimed = 0; + int freed = 0; - // Iterate through the results - for( int t = 2; t <= dynlen( aResult ); t++) + for (int i=1; i<= dynlen(objectNames);i++) { - aDP = aResult[t][1]; - strName = aResult[t][2]; - tClaimDate = aResult[t][3]; - tFreeDate = aResult[t][4]; - - strDP=dpSubStr(aDP,DPSUB_DP); - -// LOG_DEBUG("claimManager.ctl:claimManager_queryConnectClaim_Callback| strDP : "+ strDP); -// LOG_DEBUG("claimManager.ctl:claimManager_queryConnectClaim_Callback| strName : "+strName); -// LOG_DEBUG("claimManager.ctl:claimManager_queryConnectClaim_Callback| ClaimDate : "+year(tClaimDate)); -// LOG_DEBUG("claimManager.ctl:claimManager_queryConnectClaim_Callback| FreeDate : "+year(tFreeDate)); - - - // We are unclaimed when the date == 1970 and the freedate == 1970 - if (year(tClaimDate) == 1970 && year(tFreeDate) == 1970) { - bClaimed = false; - } else { - bClaimed = true; + time claim = claimDates[i]; + time free = freeDates[i]; + dynAppend(claimedObjectNames,objectNames[i]); + dynAppend(claimedDPNames,DPNames[i]); + if (period(claim) == 0 && period(free) == 0) + { + unused += 1; } - - // Do we already have this name - iPos = dynContains( strClaimDPName, strDP ); - - LOG_TRACE("claimManager.ctl:claimManager_queryConnectClaim_Callback| found old claim at postion: "+ iPos); - - - // When we have the claim, and the datapoint is now 'not claimed' - // then - if( !bClaimed && (iPos > 0)) + else if (period(claim) == 0) { - dynRemove( strClaimDPName , iPos ); - dynRemove( strClaimObjectName , iPos ); + freed += 1; } - - // When we do not have the item and it gets 'Claimed' - // then we have to add it ! - if( bClaimed && (iPos < 1 )) + else if (period(free) == 0) { - dynAppend( strClaimDPName , strDP ); - dynAppend( strClaimObjectName , strName ); - } else if( bClaimed && (iPos > 0 )) { - // When we do have the item and it gets 'Claimed' - // then we have to alter the - strClaimObjectName[iPos] = strName; + claimed += 1; + } + else + { + full = true; } } - LOG_DEBUG("writing connectClaimsFinished"); + + + g_unusedClaims = unused; + g_usedClaims = claimed; + g_freeClaims = freed; + + strClaimDPName = claimedDPNames; + strClaimObjectName = claimedObjectNames; + + LOG_DEBUG( "claimManager.ctl:claimManager_updateCacheClaims|writing connectClaimsFinished"); if ( g_initializing ) { writeInitProcess("connectClaimsFinished"); } diff --git a/MAC/Navigator2/scripts/libs/navFunct.ctl b/MAC/Navigator2/scripts/libs/navFunct.ctl index 598aad96556924910cdf63977c4b135566aa5316..bb2f74ad007f563a71aa15a0a752308848ce597d 100644 --- a/MAC/Navigator2/scripts/libs/navFunct.ctl +++ b/MAC/Navigator2/scripts/libs/navFunct.ctl @@ -1476,6 +1476,7 @@ void navFunct_fillHardwareTree() { string dp=""; // add Stations + if (dynlen(g_stationList) > 1 ) { for (int i = 1; i <= dynlen(g_stationList); i++) { // for ease of selection we use stationlike objects on the main google hardware panels, such @@ -1544,6 +1545,15 @@ void navFunct_fillHardwareTree() { } else { + // add PowerUnits + if (dynlen(g_powerUnitList) > 0) { + for (int i = 1; i <= dynlen(g_powerUnitList); i++) { + dp = station+":LOFAR_PIC_POWEC"+g_powerUnitList[i]; + dynAppend(result,connectTo+",POWEC"+g_powerUnitList[i]+","+dp); + } + lvl="PowerUnit"; + } + // add Cabinets if (dynlen(g_cabinetList) > 0) { for (int i = 1; i <= dynlen(g_cabinetList); i++) { @@ -1824,6 +1834,7 @@ void navFunct_clearGlobalLists() { dynClear(g_subrackList); dynClear(g_uriBoardList); dynClear(g_uniBoardList); + dynClear(g_powerUnitList); dynClear(g_FPGAList); dynClear(g_RSPList); dynClear(g_TBBList); diff --git a/MAC/Navigator2/scripts/libs/navigator.ctl b/MAC/Navigator2/scripts/libs/navigator.ctl index d868b5b3d0afd19c216513c64126a100e674d7f2..0c38275bb24d32894f49b1fda181340430ac78b5 100644 --- a/MAC/Navigator2/scripts/libs/navigator.ctl +++ b/MAC/Navigator2/scripts/libs/navigator.ctl @@ -80,6 +80,7 @@ global dyn_int g_TBBList; // holds valid TBB's for choices in the v global dyn_int g_RCUList; // holds valid RCU's for choices in the viewBox global dyn_int g_HBAList; // holds valid HBAAntenna's for choices in the viewBox global dyn_int g_LBAList; // holds valid LBAAntenna's for choices in the viewBox +global dyn_int g_powerUnitList; // holds valid PowerUnits for choices in the viewBox // CEP based globals global dyn_int g_OSRackList; // holds valid Offline/Storageracks for choices in view global dyn_int g_locusNodeList; // holds valid storagenodes for choices in view diff --git a/MAC/Navigator2/scripts/readStationConfigs.ctl b/MAC/Navigator2/scripts/readStationConfigs.ctl index 7181cadc85b1b824a646eb02ae3ab39150e7a957..38d70a65fd9b99bedcb7667d628fd8138a3367ca 100644 --- a/MAC/Navigator2/scripts/readStationConfigs.ctl +++ b/MAC/Navigator2/scripts/readStationConfigs.ctl @@ -39,8 +39,13 @@ * and Antenna positions in OL-NB-Height Offsets from the fieldCenter. * As FieldCenter now the GPS coordinates are taken, this is not correct * + * With the new LCU's we need to use the new Blitz 0.10 version that has a change in array use. + * So for now a Blitz010 bool is used to check if it is the new Blitz or not. + * The files for the new Blitz version are indicated with the line : + * + * # Blitz010 formatted + * * For future compatibility we have to consider other earth coordinates also - * RemoteStation.conf * This will fill the RemoteStation point with all data available for this station. * @@ -50,6 +55,8 @@ global bool bDebug = false; global int nr_HBA=0; global int nr_LBA=0; + global int nr_POWECS=0; + global string stationIP = ""; global bool norVecLBAFound=false; global bool norVecHBAFound=false; global bool norVecHBA0Found=false; @@ -64,6 +71,7 @@ global bool deltasHBAFound=false; global bool centerHBA0Found=false; global bool centerHBA1Found=false; + global bool Blitz010=false; main() @@ -83,7 +91,6 @@ main() return; } -// string strAntArrayConfFile = strDataDir+"AntennaArrays.conf"; string strAntArrayConfFile = strDataDir+"AntennaField.conf"; string strRemoteStationConfFile = strDataDir+"RemoteStation.conf"; @@ -100,6 +107,10 @@ main() string str1="",str2=""; sscanf(dynStr_fileContent[index++],"%s %s",str1,str2); if (strlen(str1) <= 0 || str1[0] == " " || str1[0] == "#") { + if (strpos(dynStr_fileContent[index-1],"Blitz-0.10 formatted") >= 0) { + DebugN("Blitz: ", str2); + Blitz010=true; + } continue; } @@ -208,6 +219,14 @@ main() } } + if (strpos(dynStr_fileContent[index],"RS.STATION_IP")>-1) { + dyn_string value = strsplit(dynStr_fileContent[index],"="); + if (dynlen(value) > 1) { + stationIP = value[2]; + dpSet("LOFAR_PIC_StationInfo.stationIP",stationIP); + } + } + if (strpos(dynStr_fileContent[index],"RS.N_RSPBOARDS")>-1) { dyn_string value = strsplit(dynStr_fileContent[index],"="); if (dynlen(value) > 1) { @@ -236,6 +255,14 @@ main() } } + if (strpos(dynStr_fileContent[index],"RS.N_POWECS")>-1) { + dyn_string value = strsplit(dynStr_fileContent[index],"="); + if (dynlen(value) > 1) { + nr_POWECS = value[2]; + dpSet("LOFAR_PIC_StationInfo.nrOfPowerUnits",nr_POWECS); + } + } + if (strpos(dynStr_fileContent[index],"RS.HBA_SPLIT")>-1) { dyn_string value = strsplit(dynStr_fileContent[index],"="); if (dynlen(value) > 1) { @@ -278,9 +305,32 @@ main() } } } - - } + + // set POWEC IP address for all available POWECS. + dyn_string ipparts = strsplit(stationIP,"."); + string ip = ipparts[1] + "." + ipparts[2] + "." + ipparts[3] + "."; + + dpSet("_2_SNMPAgent_1.Access.IPAddress",ip+"5"); + dpSet("LOFAR_PIC_POWEC0.nrOfModules:_address.._active",1); + dpSet("LOFAR_PIC_POWEC0.voltage:_address.._active",1); + dpSet("LOFAR_PIC_POWEC0.current:_address.._active",1); + dpSet("LOFAR_PIC_POWEC0.temperature:_address.._active",1); + dpSet("LOFAR_PIC_POWEC0.OK:_address.._active",1); + if (nr_POWECS == 2) { + dpSet("_2_SNMPAgent_2.Access.IPAddress",ip+"7"); + dpSet("LOFAR_PIC_POWEC1.nrOfModules:_address.._active",1); + dpSet("LOFAR_PIC_POWEC1.voltage:_address.._active",1); + dpSet("LOFAR_PIC_POWEC1.current:_address.._active",1); + dpSet("LOFAR_PIC_POWEC1.temperature:_address.._active",1); + dpSet("LOFAR_PIC_POWEC1.OK:_address.._active",1); + } else if (nr_POWECS == 1) { + dpSet("LOFAR_PIC_POWEC1.nrOfModules:_address.._active",0); + dpSet("LOFAR_PIC_POWEC1.voltage:_address.._active",0); + dpSet("LOFAR_PIC_POWEC1.current:_address.._active",0); + dpSet("LOFAR_PIC_POWEC1.temperature:_address.._active",0); + dpSet("LOFAR_PIC_POWEC1.OK:_address.._active",0); + } } @@ -318,7 +368,13 @@ dyn_string lto_getFile_asDynStr(string aFileName) { void processNormalVector(string aS) { float fX=0,fY=0,fZ=0; - sscanf(dynStr_fileContent[index++],"%*d %*s %lf %lf %lf",fX,fY,fZ); + if (Blitz010) { + int dummy, lenx, leny; + sscanf(dynStr_fileContent[index++],"(%d,%d) x (%d,%d) %*s %lf %lf %lf",dummy,lenx,dummy,leny,fX,fY,fZ); + } else { + sscanf(dynStr_fileContent[index++],"%*d %*s %lf %lf %lf",fX,fY,fZ); + } + if (bDebug) DebugN("Reading NORMAL_VECTOR "+aS+" X,Y,Z :" + fX + " " + fY + " " + fZ); if (aS == "LBA" ) { norVecLBAFound=true; @@ -346,8 +402,15 @@ void processNormalVector(string aS) { void processRotationMatrix(string aS) { dyn_float fX,fY,fZ; int nr_rows=0; - // read nr of rows - sscanf(dynStr_fileContent[index++],"%d",nr_rows); + + if (Blitz010) { + int dummy; + // read nr of rows + sscanf(dynStr_fileContent[index++],"(%d,%d)",dummy,nr_rows); + nr_rows+=1; + } else { + sscanf(dynStr_fileContent[index++],"%d",nr_rows); + } if (bDebug) DebugN("index: "+(index-1)+" nr_rows: "+nr_rows); for (int i = 1; i <= nr_rows; i++) { sscanf(dynStr_fileContent[index++],"%lf %lf %lf",fX[i],fY[i],fZ[i]); @@ -375,13 +438,17 @@ void processRotationMatrix(string aS) { "LOFAR_PIC_StationInfo.HBA.HBA0.RotationMatrix.Y",fY, "LOFAR_PIC_StationInfo.HBA.HBA0.RotationMatrix.Z",fZ); } - - } void processFieldCenter(string aS) { + float fX=0,fY=0,fZ=0; - sscanf(dynStr_fileContent[index++],"%*d %*s %lf %lf %lf",fX,fY,fZ); + if (Blitz010) { + int dummy; + sscanf(dynStr_fileContent[index++],"(%d,%d) %*s %f %f %f",dummy, dummy, fX, fY, fZ); + } else { + sscanf(dynStr_fileContent[index++],"%*d %*s %lf %lf %lf",fX,fY,fZ); + } if (bDebug) DebugN("Reading fieldcenter "+aS+"X,Y,Z:" + fX + " " + fY + " " + fZ); if (aS== "LBA") { centerLBAFound=true; @@ -407,6 +474,7 @@ void processFieldCenter(string aS) { } void processFieldDeltas(string aS) { + int nr_ofAnt = 0; float deltaX; float deltaY; @@ -415,10 +483,17 @@ void processFieldDeltas(string aS) { dyn_float antConfFileY; dyn_float antConfFileZ; - - // read nr of antennas - sscanf(dynStr_fileContent[index++],"%d",nr_ofAnt); - + if (Blitz010) { + int dummy; + + // read nr of antennas + sscanf(dynStr_fileContent[index++],"(%d,%d)",dummy, nr_ofAnt); + nr_ofAnt+=1; + } else { + // read nr of antennas + sscanf(dynStr_fileContent[index++],"%d",nr_ofAnt); + } + if (aS== "LBA") { deltasLBAFound=true; nr_LBA=nr_ofAnt; @@ -476,8 +551,7 @@ void calcRotated(string aS) { float centerX,centerY,centerZ; float X,Y,Z; float x1=0,x2=0,y1=0,y2=0,x3=0,x4=0,y3=0,y4=0; - - + if (aS=="LBA") { dpGet("LOFAR_PIC_StationInfo.LBA.RotationMatrix.X",rotX,"LOFAR_PIC_StationInfo.LBA.RotationMatrix.Y",rotY,"LOFAR_PIC_StationInfo.LBA.RotationMatrix.Z",rotZ, "LOFAR_PIC_StationInfo.LBA.centerX",centerX,"LOFAR_PIC_StationInfo.LBA.centerY",centerY,"LOFAR_PIC_StationInfo.LBA.centerZ",centerZ); diff --git a/MAC/Navigator2/scripts/setSumAlerts.ctl b/MAC/Navigator2/scripts/setSumAlerts.ctl new file mode 100644 index 0000000000000000000000000000000000000000..844e90f4441e7872278adcf7259fae5bfb1212c3 --- /dev/null +++ b/MAC/Navigator2/scripts/setSumAlerts.ctl @@ -0,0 +1,310 @@ + + + +bool bDebug; + +void main() +{ + // connect to debugflag to be able to switch debug on/off during run + if (dpExists("scriptInfo.setSumAlerts.debug")) { + dpConnect("debugCB",true,"scriptInfo.setSumAlerts.debug"); + } else { + DebugTN("setSumAlerts.ctl:main|scriptInfo.setSumAlerts.debugpoint not found in Database"); + } + + delay(0,100); + + if (dpExists("scriptInfo.setSumAlerts.runDone")) { + dpConnect("setSumAlerts",true,"scriptInfo.setSumAlerts.runDone"); + } else { + DebugTN("setSumAlerts.ctl:main|scriptInfo.setSumAlerts.runDone point not found in Database"); + } + + +} + + +private void debugCB(string dp1, bool debug) +{ + bDebug = debug; +} + + +private void setSumAlerts( string strDPE, bool bRunDone ) +{ + int x, y; + string strDPE; + dyn_string dsParentDPEs, dsChilds, dsChildDPEs; + + // If we're already done: do nothing + if( bRunDone ) + return; + + DebugTN("setSumAlerts.ctl:main|start set of sumAlerts"); + + // Get all DPE's with a 'childSumAlert' + dsParentDPEs = dpNames( "*.**.childSumAlert" ); + + dynSort(dsParentDPEs); + + // Go through sumalerts + for( x=1; x<=dynlen(dsParentDPEs); x++ ) + { + bool bRetVal1, bRetVal2, bLeaf; + int iRetVal, iAlertHdlType; + string strDPE; + dyn_string dsChilds, dsChildDPEs; + + // Use DPE part, and remove last parts (normally '.state.childSumAlert') + strDPE = dpSubStr( dsParentDPEs[x], DPSUB_DP_EL ); + strDPE = RemoveLastDpeParts(strDPE); + + // Skip master datapoints and leaf=True points + if( patternMatch( "*_mp*", strDPE )) + { + continue; + } + + if( strDPE == "" ) + { + continue; + } + + dpGet(strDPE+".status.leaf",bLeaf); + if (bLeaf) + { + continue; + } + + + if (bDebug) + { + DebugTN("Working on: " + strDPE); + } + + // Get childs of this DPE + dsChilds = GetChilds( strDPE ); + + + // For each child: get DPE's to add to sumalerts and append to list + for( y=1; y<=dynlen(dsChilds); y++ ) + { + dynAppend( dsChildDPEs, GetChildDPEs( dsChilds[y] ) ); + } + + + // Check if this DPE has an alert_hdl of type sumalert + dpGet( dsParentDPEs[x] + ":_alert_hdl.._type", iAlertHdlType ); + + if( iAlertHdlType == DPCONFIG_SUM_ALERT) + { + if (dynlen(dsChildDPEs) > 0) + { + // First deactivate the alert + dpDeactivateAlert( dsParentDPEs[x], bRetVal1 ); + if( !bRetVal1 ) + { + DebugTN( "setSumAlerts(): FAILED TO dpDeactivateAlert FOR DPE '" + dsParentDPEs[x] + "'!!" ); + } + + // No change the sumalert dplist + iRetVal = dpSet( dsParentDPEs[x] + ":_alert_hdl.._dp_list", dsChildDPEs, + dsParentDPEs[x] + ":_alert_hdl.._dp_pattern", "" ); + + dyn_errClass derrLastError = getLastError(); + if( dynlen(derrLastError) > 0 ) + { + DebugTN( "setSumAlerts(): FAILED TO dpSet FOR DPE, getLastError():", derrLastError ); + } + else if( iRetVal != 0 ) + { + DebugTN( "setSumAlerts(): FAILED TO dpSet FOR DPE, iRetVal = " + iRetVal ); + } + + // Activate alert again + dpActivateAlert( dsParentDPEs[x], bRetVal2 ); + if( !bRetVal2 ) + { + DebugTN( "setSumAlerts(): FAILED TO dpActivateAlert FOR DPE '" + dsParentDPEs[x] + "'!!" ); + } + + // Show if we're succesfull + if( bRetVal1 && bRetVal2 && ( iRetVal == 0 ) ) + { + if( bDebug ) + { + DebugTN( "setSumAlerts(): SumAlerts for DPE '" + dsParentDPEs[x] + "' succesfully set to " + dynlen(dsChildDPEs) + " child DPEs" ); + } + } + } + } + else + { + DebugTN( "setSumAlerts(): DPE '" + dsParentDPEs[x] + "' DOESN'T HAVE AN ALERT-HANDLE OR NOT OF TYPE SUM-ALERT!!" ); + } + + // Small delay to give some time and limit number of events + delay(0,10); + + } + + dpSet( "scriptInfo.setSumAlerts.runDone", true ); + DebugTN("setSumAlerts.ctl:main|set of sumAlerts done"); + +} + + + + + +private dyn_string GetChilds( string strDPE ) +{ + int x, iLevel; + string strDP, strParent, strChildDPE; + dyn_string dsDPEs, dsChilds; + + strParent = strDPE; + + strDP = dpSubStr( strDPE, DPSUB_DP ); + strDPE = dpSubStr( strDPE, DPSUB_DP_EL ); + + iLevel = GetDpeLevel( strDPE ); + + // Get childs of this DPE + if( patternMatch( "*.*", strDPE ) ) + { + // Only get child DPE's + dynAppend( dsDPEs, dpNames( strDPE + ".**.childSumAlert" ) ); + } + else + { + // Get child DP's and child DPE's + dynAppend( dsDPEs, dpNames( strDP + "_*.*.childSumAlert" ) ); + dynAppend( dsDPEs, dpNames( strDPE + ".**.childSumAlert" ) ); + } + + for( x=dynlen(dsDPEs); x>=1; x-- ) + { + strChildDPE = dpSubStr( dsDPEs[x], DPSUB_DP_EL ); + strChildDPE = RemoveLastDpeParts(strChildDPE); + + // Remove master datapoints + if( patternMatch( "*_mp*", strChildDPE ) ) + { + continue; + } + + + if( GetDpeLevel( strChildDPE ) <= 1 ) + { + // If this DP is not a direct child, but a childs child, skip it + if( patternMatch( strParent + "_*_*", strChildDPE ) ) + { + continue; + } + } + else + { + // Check level of this DPE, if more then 2 deeper then given leven, skip it (so only next level is used) + if( GetDpeLevel( strChildDPE ) > ( iLevel + 1 ) ) + { + continue; + } + } + + // Remove our given DPE + if( strChildDPE == strDPE ) + { + continue; + } + + + dynAppend( dsChilds, strChildDPE ); + + } + + + dynUnique( dsChilds ); + dynSort( dsChilds ); + + return dsChilds; +} + + + +private dyn_string GetChildDPEs( string strDPE ) +{ + int x, iLevel; + dyn_string dsDPEs, dsChildDPEs; + + iLevel = GetDpeLevel(strDPE); + + // Get 'state' and 'childSumAlert' DPE + dynAppend( dsDPEs, dpNames( strDPE + ".*.state" ) ); + dynAppend( dsDPEs, dpNames( strDPE + ".*.childSumAlert" ) ); + + // Only append DPE's if they are 2 levels deeper then given DPE + for( x=1; x<=dynlen(dsDPEs); x++ ) + { + dsDPEs[x] = dpSubStr( dsDPEs[x], DPSUB_DP_EL ); + + if( GetDpeLevel(dsDPEs[x]) == ( iLevel + 2 ) ) + { + dynAppend( dsChildDPEs, dsDPEs[x] ); + } + + } + + return dsChildDPEs; +} + + +private string RemoveLastDpeParts( string strDPE ) +{ + dyn_string dsParts; + + strDPE = dpSubStr( strDPE, DPSUB_DP_EL ); + + // Split into parts + dsParts = strsplit( strDPE, "." ); + + // Remove 'first' last part + if( dynlen( dsParts ) >= 2 ) + { + dynRemove( dsParts, dynlen(dsParts) ); + } + + // Remove 'second' last part + if( dynlen( dsParts ) >= 2 ) + { + dynRemove( dsParts, dynlen(dsParts) ); + } + + // Convert back to DPE + strDPE = dynStringToString( dsParts, "." ); + + return strDPE; +} + + + + + +private int GetDpeLevel( string strDPE ) +{ + dyn_string dsParts; + + strDPE = dpSubStr( strDPE, DPSUB_DP_EL ); + + dsParts = strsplit( strDPE, "." ); + + // This is a DPE? + if( dynlen(dsParts) <= 1 ) + return 0; + + return ( dynlen(dsParts) - 1 ); +} + + + + diff --git a/MAC/Navigator2/scripts/transferMPs.ctl b/MAC/Navigator2/scripts/transferMPs.ctl index e56676752fdcc6ff768db2cc2cfa8b185ba73d20..da806d60e5abb30953f1fb073753313c17dc10b2 100644 --- a/MAC/Navigator2/scripts/transferMPs.ctl +++ b/MAC/Navigator2/scripts/transferMPs.ctl @@ -12,7 +12,7 @@ void main() if (dpExists("scriptInfo.transferMPs.debug")) { dpConnect("debugCB",true,"scriptInfo.transferMPs.debug"); } else { - DebugTN("transferMPs.ctl:main|scriptInfo.transferMPs.debugpoint not found in Database"); + DebugTN("transferMPs.ctl:main|scriptInfo.transferMPs.debugpoint not found in bbase"); } if (dpExists("scriptInfo.transferMPs.runDone")) { @@ -49,10 +49,11 @@ private void startTransferMP(string dp1, bool done ) { string sDestinationDPE; if (done) return; + + startTransferMP_Embedded(); - DebugTN("transferMPs.ctl:main|start transfer of MPconfigs to all DP's"); + DebugTN("transferMPs.ctl:main|start transfer of MPconfigs to all DP's"); - string query="SELECT '_original.._value' FROM '_mp_*'"; dyn_dyn_anytype tab; dyn_string dps; @@ -67,12 +68,12 @@ private void startTransferMP(string dp1, bool done ) { } } } - + for (k=1;k <= dynlen(dps); k++) { string dpstr = dps[k]; dyn_string dsDpes = dpNames( dpstr + ".**"), dsDps = dpNames("*",dpTypeName(dpstr)); - + // no datapoints found if ( dynlen(dsDps) > 1 ) { @@ -81,6 +82,7 @@ private void startTransferMP(string dp1, bool done ) { ll = l * (l1 - 1); // !!! dynlen(dsDpes) * dynlen(dsDps) verwenden mit der 2-Sek-Verz�gerung + for ( i = 1; i <= l; i++ ) { if ( strpos(dsDpes[i],".") < 1 ) dsDpes[i] += "."; dsConfigs = dpNames( dsDpes[i] + ":*" ); @@ -97,13 +99,16 @@ private void startTransferMP(string dp1, bool done ) { sDestinationDPE = dsDpes[i]; strreplace( sDestinationDPE, dpSubStr(dsDpes[i], DPSUB_DP), dpSubStr(dsDps[j], DPSUB_DP)); daCheckDPE(sDestinationDPE); - dpCopyConfig(dsDpes[i], sDestinationDPE, dsConfigs, iError); } } } } dpSet("scriptInfo.transferMPs.runDone",true); + + // fire setSumAlerts now that the database is complete + dpSet("scriptInfo.setSumAlerts.runDone",false); + DebugTN("MPTransfer Done."); } @@ -209,3 +214,99 @@ bool skipDP(string dp) { if (strpos(dp,"MODE_CMD") > -1) return true; return false; } + + + + + +void startTransferMP_Embedded() +{ + int x, y; + dyn_dyn_anytype ddaData; + dyn_dyn_string ddsRefs; + dyn_string dsDPs; + dyn_string dsDPTs; + + DebugTN("========== transferMPs.ctl:main|start transfer of embedded MPconfigs to all master DP's"); + + string strQuery = "SELECT '_original.._value' FROM '_mp_*'"; + dpQuery( strQuery,ddaData ); + + for( x=2; x<=dynlen(ddaData); x++) + { + string strDP = dpSubStr( ddaData[x][1], DPSUB_DP ); + string strDPT = dpTypeName(strDP); + + if( skipDP( strDP ) ) + { + continue; + } + + if (dynContains(dsDPTs, strDPT)) + { + continue; + } + + dynAppend(dsDPTs,strDPT); + + + // Get all DP-Types that has reference to this DP-type + ddsRefs = dpGetRefsToDpType( strDPT ); + + for( y=1; y<=dynlen(ddsRefs); y++ ) + { + if( dynlen(ddsRefs[y]) != 2 ) + continue; + + string strDPTsource = strDPT; + string strDPTtarget = ddsRefs[y][1]; + string strDPEtarget = ddsRefs[y][2]; + + CopyEmbbeddedMasterDP( strDPTsource, strDPTtarget, strDPEtarget ); + } + } + + DebugTN("========== transfer of embedded MPconfigs to all master DP's done"); + +} + + + +// Copies the master-datapoint configs of the given datapoint type 'strDPTsource' to the master-datapoint of the given type 'strDPTtarget' +void CopyEmbbeddedMasterDP( string strDPTsource, string strDPTtarget, string strDPTtargetElement ) +{ + int x, iError; + string strDPsource, strDPtarget, strDPEsource, strDPEtarget; + dyn_string dsDPEsSource; + dyn_string dsConfigs = makeDynString( "_distrib", "_address", "_archive", "_alert_hdl", + "_cmd_conv", "_msg_conv", + "_default", "_dp_fct", "_pv_range", + "_smooth", "_u_range", "_auth", + "_alert_class", "_original" ); + + + strDPsource = "_mp_" + strDPTsource; + strDPtarget = "_mp_" + strDPTtarget; + + strDPsource = dpSubStr( strDPsource, DPSUB_DP ); + strDPtarget = dpSubStr( strDPtarget, DPSUB_DP ); + + if( !dpExists( strDPsource ) ) + return; + + if( !dpExists( strDPtarget ) ) + return; + + dsDPEsSource = dpNames( strDPsource + ".**" ); + + if (bDebug) DebugN( "CopyEmbbeddedMasterDP(): Copy config from " + dynlen(dsDPEsSource) + " DPE's of '" + strDPsource + "' to '" + strDPtarget + "." + strDPTtargetElement +"'" ); + for( x=1; x<=dynlen(dsDPEsSource); x++ ) + { + strDPEsource = dpSubStr( dsDPEsSource[x], DPSUB_DP_EL ); + + strDPEtarget = strDPtarget + "." + strDPTtargetElement + strltrim( strDPEsource, strDPsource ); + + if (bDebug)DebugN( "Copy " + strDPEsource + " -> " + strDPEtarget ); + dpCopyConfig( strDPEsource, strDPEtarget, dsConfigs, iError); + } +} diff --git a/MAC/Services/CMakeLists.txt b/MAC/Services/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..303a12a5b2ea42a0a32445d08aa804054ca1ed11 --- /dev/null +++ b/MAC/Services/CMakeLists.txt @@ -0,0 +1,6 @@ +# $Id$ + +lofar_package(MAC_Services 1.0 DEPENDS PyMessaging OTDB_Services pyparameterset Docker ResourceAssignmentService) + +add_subdirectory(src) +add_subdirectory(test) diff --git a/MAC/Services/src/CMakeLists.txt b/MAC/Services/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..692b7b5ce4ab157ee88c3d60c29c3c350723c0ab --- /dev/null +++ b/MAC/Services/src/CMakeLists.txt @@ -0,0 +1,15 @@ +# $Id$ + +lofar_add_bin_scripts( + pipelinecontrol +) + +python_install( + PipelineControl.py + DESTINATION lofar/mac +) + +# supervisord config files +install(FILES + pipelinecontrol.ini + DESTINATION etc/supervisord.d) diff --git a/MAC/Services/src/PipelineControl.py b/MAC/Services/src/PipelineControl.py new file mode 100755 index 0000000000000000000000000000000000000000..d038f4ee475720578e28c5147d8dd0ecb61359be --- /dev/null +++ b/MAC/Services/src/PipelineControl.py @@ -0,0 +1,637 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id$ +""" +Daemon that starts/stops pipelines based on their status in OTDB. + +The execution chains are as follows: + +----------------------------- + Starting a pipeline +----------------------------- + +[SCHEDULED] -> PipelineControl schedules + + runPipeline.sh <obsid> + + and + + setOTDBTreeStatus -o <obsid> -s aborted + + using two SLURM jobs, guaranteeing that pipelineAborted.sh is + called in the following circumstances: + + - runPipeline.sh wrapper cannot finish (bash bugs, etc) + - runPipeline.sh job is cancelled in the queue + + State is set to [QUEUED]. + +(runPipeline.sh) -> Calls + - state <- [ACTIVE] + - getParset + - (run pipeline) + - success: + - state <- [COMPLETING] + - (wrap up) + - state <- [FINISHED] + - failure: + - state <- [ABORTED] + +(setOTDBTreeStatus) -> Calls + - state <- [ABORTED] + +----------------------------- + Stopping a pipeline +----------------------------- + +[ABORTED] -> Cancels SLURM job associated with pipeline, causing + a cascade of job terminations of successor pipelines. +""" + +from lofar.messaging import FromBus, ToBus, RPC, EventMessage +from lofar.parameterset import PyParameterValue +from lofar.sas.otdb.OTDBBusListener import OTDBBusListener +from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_BUSNAME, DEFAULT_OTDB_SERVICE_BUSNAME +from lofar.sas.otdb.otdbrpc import OTDBRPC +from lofar.common.util import waitForInterrupt +from lofar.messaging.RPC import RPCTimeoutException, RPCException +from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as DEFAULT_RAS_SERVICE_BUSNAME + +import subprocess +import datetime +import pipes +import os +import re +from socket import getfqdn + +import logging +logger = logging.getLogger(__name__) + +def runCommand(cmdline, input=None): + logger.info("runCommand starting: %s", cmdline) + + # Start command + proc = subprocess.Popen( + cmdline, + stdin=subprocess.PIPE if input else file("/dev/null"), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + universal_newlines=True + ) + + # Feed input and wait for termination + logger.debug("runCommand input: %s", input) + stdout, _ = proc.communicate(input) + logger.debug("runCommand output: %s", stdout) + + # Check exit status, bail on error + if proc.returncode != 0: + logger.warn("runCommand(%s) had exit status %s with output: %s", cmdline, proc.returncode, stdout) + raise subprocess.CalledProcessError(proc.returncode, cmdline) + + # Return output + return stdout.strip() + +""" Prefix that is common to all parset keys, depending on the exact source. """ +PARSET_PREFIX="ObsSW." + +class Parset(dict): + def predecessors(self): + """ Extract the list of predecessor obs IDs from the given parset. """ + + key = PARSET_PREFIX + "Observation.Scheduler.predecessors" + strlist = PyParameterValue(str(self[key]), True).getStringVector() + + # Key contains "Lxxxxx" values, we want to have "xxxxx" only + result = [int(filter(str.isdigit,x)) for x in strlist] + + return result + + def isObservation(self): + return self[PARSET_PREFIX + "Observation.processType"] == "Observation" + + def isPipeline(self): + return not self.isObservation() + + def processingCluster(self): + return self[PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.clusterName"] or "CEP2" + + def processingPartition(self): + result = self[PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.clusterPartition"] or "cpu" + if '/' in result: + logger.error('clusterPartition contains invalid value: %s. Defaulting clusterPartition to \'cpu\'', result) + return 'cpu' + return result + + def processingNumberOfCoresPerTask(self): + result = int(self[PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.numberOfCoresPerTask"]) or "21" + if result != 21: + logger.warn('Invalid Observation.Cluster.ProcessingCluster.numberOfCoresPerTask: %s, defaulting to %s', result, 21) + return 21 + + def processingNumberOfTasks(self): + """ Parse the number of nodes to allocate from "Observation.Cluster.ProcessingCluster.numberOfTasks", + which can have either the format "{number}" or "{min}-{max}". """ + + defaultValue = "12-25" + parsetValue = self[PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.numberOfTasks"].strip() + + if "-" in parsetValue: + # min,max + _min, _max = parsetValue.split("-") + + # collapse if not min <= max + if _min > _max: + result = _min + else: + result = "%s-%s" % (_min, _max) + else: + # plain number + result = int(parsetValue) + + # apply bound + if result < 1 or result > 50: + result = defaultValue + + if result != parsetValue: + logger.error('Invalid Observation.Cluster.ProcessingCluster.numberOfTasks: %s, defaulting to %s', parsetValue, result) + + return result + + @staticmethod + def dockerRepository(): + return "nexus.cep4.control.lofar:18080" + + @staticmethod + def defaultDockerImage(): + return runCommand("docker-template", "lofar-pipeline:${LOFAR_TAG}") + + @staticmethod + def defaultDockerTag(): + return runCommand("docker-template", "${LOFAR_TAG}") + + def dockerImage(self): + # Return the version set in the parset, and fall back to our own version. + image = self[PARSET_PREFIX + "Observation.ObservationControl.PythonControl.softwareVersion"] + + if not image: + return self.defaultDockerImage() + + if ":" in image: + return image + + # Insert our tag by default + return "%s:%s" % (image, self.defaultDockerTag()) + + def otdbId(self): + return int(self[PARSET_PREFIX + "Observation.otdbID"]) + + def description(self): + return "%s - %s" % (self[PARSET_PREFIX + "Observation.Campaign.name"],self[PARSET_PREFIX + "Observation.Scheduler.taskName"]) + + +class Slurm(object): + def __init__(self, headnode="head01.cep4.control.lofar"): + self.headnode = headnode + + # TODO: Derive SLURM partition name + self.partition = "cpu" + + def _runCommand(self, cmdline, input=None): + cmdline = "ssh %s %s" % (self.headnode, cmdline) + return runCommand(cmdline, input) + + def submit(self, jobName, cmdline, sbatch_params=None): + if sbatch_params is None: + sbatch_params = [] + + script = """#!/bin/bash -v +{cmdline} +""".format(cmdline = cmdline) + + stdout = self._runCommand("sbatch --partition=%s --job-name=%s %s" % (self.partition, jobName, " ".join(sbatch_params)), script) + + # Returns "Submitted batch job 3" -- extract ID + match = re.search("Submitted batch job (\d+)", stdout) + if not match: + return None + + return match.group(1) + + def cancel(self, jobName): + self._runCommand("scancel --jobname %s" % (jobName,)) + + def isQueuedOrRunning(self, jobName): + stdout = self._runCommand("sacct --starttime=2016-01-01 --noheader --parsable2 --format=jobid --name=%s --state=PENDING,CONFIGURING,RUNNING,RESIZING,COMPLETING,SUSPENDED" % (jobName,)) + + return stdout != "" + +class PipelineDependencies(object): + class TaskNotFoundException(Exception): + """ Raised when a task cannot be found in the RADB. """ + pass + + def __init__(self, ra_service_busname=DEFAULT_RAS_SERVICE_BUSNAME, otdb_service_busname=DEFAULT_OTDB_SERVICE_BUSNAME): + self.rarpc = RARPC(busname=ra_service_busname) + self.otdbrpc = OTDBRPC(busname=otdb_service_busname) + + def open(self): + self.rarpc.open() + self.otdbrpc.open() + + def close(self): + self.rarpc.close() + self.otdbrpc.close() + + def __enter__(self): + self.open() + return self + + def __exit__(self, type, value, tb): + self.close() + + def getState(self, otdb_id): + """ + Return the status of a single `otdb_id'. + """ + return self.otdbrpc.taskGetStatus(otdb_id=otdb_id) + + def getPredecessorStates(self, otdb_id): + """ + Return a dict of {"sasid":"status"} pairs of all the predecessors of `otdb_id'. + """ + radb_task = self.rarpc.getTask(otdb_id=otdb_id) + + if radb_task is None: + raise PipelineDependencies.TaskNotFoundException("otdb_id %s not found in RADB" % (otdb_id,)) + + predecessor_radb_ids = radb_task['predecessor_ids'] + predecessor_tasks = self.rarpc.getTasks(task_ids=predecessor_radb_ids) + predecessor_states = {t["otdb_id"]: t["status"] for t in predecessor_tasks} + + logger.debug("getPredecessorStates(%s) = %s", otdb_id, predecessor_states) + + return predecessor_states + + def getSuccessorIds(self, otdb_id): + """ + Return a list of all the successors of `otdb_id'. + """ + radb_task = self.rarpc.getTask(otdb_id=otdb_id) + + if radb_task is None: + raise PipelineDependencies.TaskNotFoundException("otdb_id %s not found in RADB" % (otdb_id,)) + + successor_radb_ids = radb_task['successor_ids'] + successor_tasks = self.rarpc.getTasks(task_ids=successor_radb_ids) if successor_radb_ids else [] + successor_otdb_ids = [t["otdb_id"] for t in successor_tasks] + + logger.debug("getSuccessorIds(%s) = %s", otdb_id, successor_otdb_ids) + + return successor_otdb_ids + + def canStart(self, otdbId): + """ + Return whether `otdbId' can start, according to the status of the predecessors + and its own status. + """ + + try: + myState = self.getState(otdbId) + predecessorStates = self.getPredecessorStates(otdbId) + except PipelineDependencies.TaskNotFoundException, e: + logger.error("canStart(%s): Error obtaining task states, not starting pipeline: %s", otdbId, e) + return False + + startable = (myState == "scheduled" and all([x == "finished" for x in predecessorStates.values()])) + logger.info("canStart(%s)? state = %s, predecessors = %s, canStart = %s", otdbId, myState, predecessorStates, startable) + return startable + +class PipelineControl(OTDBBusListener): + def __init__(self, otdb_notification_busname=DEFAULT_OTDB_NOTIFICATION_BUSNAME, otdb_service_busname=DEFAULT_OTDB_SERVICE_BUSNAME, ra_service_busname=DEFAULT_RAS_SERVICE_BUSNAME, **kwargs): + super(PipelineControl, self).__init__(busname=otdb_notification_busname, **kwargs) + + self.otdb_service_busname = otdb_service_busname + self.otdbrpc = OTDBRPC(busname=otdb_service_busname) + self.dependencies = PipelineDependencies(ra_service_busname=ra_service_busname, otdb_service_busname=DEFAULT_OTDB_SERVICE_BUSNAME) + self.slurm = Slurm() + + def _setStatus(self, otdb_id, status): + self.otdbrpc.taskSetStatus(otdb_id=otdb_id, new_status=status) + + def _getParset(self, otdbId): + try: + return Parset(self.otdbrpc.taskGetSpecification(otdb_id=otdbId)["specification"]) + except RPCException, e: + # Parset not in OTDB, probably got deleted + logger.error("Cannot retrieve parset of task %s: %s", otdbId, e) + return None + + def start_listening(self, **kwargs): + self.otdbrpc.open() + self.dependencies.open() + + super(PipelineControl, self).start_listening(**kwargs) + + self._checkScheduledPipelines() + + def stop_listening(self, **kwargs): + super(PipelineControl, self).stop_listening(**kwargs) + + self.dependencies.close() + self.otdbrpc.close() + + def _checkScheduledPipelines(self): + try: + scheduled_pipelines = self.dependencies.rarpc.getTasks(task_status='scheduled', task_type='pipeline') + logger.info("Checking %s scheduled pipelines if they can start.", len(scheduled_pipelines)) + + for pipeline in scheduled_pipelines: + logger.info("Checking if scheduled pipeline otdbId=%s can start.", pipeline['otdb_id']) + try: + otdbId = pipeline['otdb_id'] + parset = self._getParset(otdbId) + if not parset or not self._shouldHandle(parset): + continue + + # Maybe the pipeline can start already + if self.dependencies.canStart(otdbId): + self._startPipeline(otdbId, parset) + else: + logger.info("Job %s was set to scheduled, but cannot start yet.", otdbId) + except Exception as e: + logger.error(e) + except Exception as e: + logger.error(e) + + @staticmethod + def _shouldHandle(parset): + try: + if not parset.isPipeline(): + logger.info("Not processing tree: is not a pipeline") + return False + + if parset.processingCluster() == "CEP2": + logger.info("Not processing tree: is a CEP2 pipeline") + return False + except KeyError as e: + # Parset not complete + logger.error("Parset incomplete, ignoring: %s", e) + return False + + return True + + @staticmethod + def _jobName(otdbId): + return str(otdbId) + + def _startPipeline(self, otdbId, parset): + """ + Schedule "docker-runPipeline.sh", which will fetch the parset and run the pipeline within + a SLURM job. + """ + + # Avoid race conditions by checking whether we haven't already sent the job + # to SLURM. Our QUEUED status update may still be being processed. + if self.slurm.isQueuedOrRunning(otdbId): + logger.info("Pipeline %s is already queued or running in SLURM.", otdbId) + return + + logger.info("***** START Otdb ID %s *****", otdbId) + + # Determine SLURM parameters + sbatch_params = [ + # Only run job if all nodes are ready + "--wait-all-nodes=1", + + # Enforce the dependencies, instead of creating lingering jobs + "--kill-on-invalid-dep=yes", + + # Maximum run time for job (7 days) + "--time=7-0", + + # Annotate the job + "--comment=%s" % pipes.quote(pipes.quote(parset.description())), + + # Lower priority to drop below inspection plots + "--nice=1000", + + "--partition=%s" % parset.processingPartition(), + "--nodes=%s" % parset.processingNumberOfTasks(), + "--cpus-per-task=%s" % parset.processingNumberOfCoresPerTask(), + + # Define better places to write the output + os.path.expandvars("--output=/data/log/pipeline-%s-%%j.log" % (otdbId,)), + ] + + def setStatus_cmdline(status): + return ( + "ssh {myhostname} '" + "source {lofarroot}/lofarinit.sh && " + "setOTDBTreeStatus -o {obsid} -s {status} -B {status_bus}" + "'" + .format( + myhostname = getfqdn(), + lofarroot = os.environ.get("LOFARROOT", ""), + obsid = otdbId, + status = status, + status_bus = self.otdb_service_busname, + )) + + try: + logger.info("Handing over pipeline %s to SLURM", otdbId) + + # Schedule runPipeline.sh + slurm_job_id = self.slurm.submit(self._jobName(otdbId), + """ +# Run a command, but propagate SIGINT and SIGTERM +function runcmd {{ +trap 'kill -s SIGTERM $PID' SIGTERM +trap 'kill -s SIGINT $PID' SIGINT + +"$@" & +PID=$! +wait $PID # returns the exit status of "wait" if interrupted +wait $PID # returns the exit status of $PID +CMDRESULT=$? + +trap - SIGTERM SIGINT + +return $CMDRESULT +}} + +# print some info +echo Running on $SLURM_NODELIST + +# notify OTDB that we're running +runcmd {setStatus_active} + +# notify ganglia +wget -O - -q "http://ganglia.control.lofar/ganglia/api/events.php?action=add&start_time=now&summary=Pipeline {obsid} ACTIVE&host_regex=" + +# run the pipeline +runcmd docker-run-slurm.sh --rm --net=host \ + -e LOFARENV={lofarenv} \ + -v $HOME/.ssh:$HOME/.ssh:ro \ + -e SLURM_JOB_ID=$SLURM_JOB_ID \ + -v /data:/data \ + {image} \ +runPipeline.sh -o {obsid} -c /opt/lofar/share/pipeline/pipeline.cfg.{cluster} -P {parset_dir} +RESULT=$? + +# notify that we're tearing down +runcmd {setStatus_completing} + +if [ $RESULT -eq 0 ]; then + # wait for MoM to pick up feedback before we set finished status + runcmd sleep 60 + + # if we reached this point, the pipeline ran succesfully + runcmd {setStatus_finished} + + # notify ganglia + wget -O - -q "http://ganglia.control.lofar/ganglia/api/events.php?action=add&start_time=now&summary=Pipeline {obsid} FINISHED&host_regex=" +fi + +# report status back to SLURM +echo "Pipeline exited with status $RESULT" +exit $RESULT + """.format( + lofarenv = os.environ.get("LOFARENV", ""), + obsid = otdbId, + parset_dir = "/data/parsets", + repository = parset.dockerRepository(), + image = parset.dockerImage(), + cluster = parset.processingCluster(), + + setStatus_active = setStatus_cmdline("active"), + setStatus_completing = setStatus_cmdline("completing"), + setStatus_finished = setStatus_cmdline("finished"), + ), + + sbatch_params=sbatch_params + ) + logger.info("Scheduled SLURM job %s for otdb_id=%s", slurm_job_id, otdbId) + + # Schedule pipelineAborted.sh + logger.info("Scheduling SLURM job for pipelineAborted.sh") + slurm_cancel_job_id = self.slurm.submit("%s-abort-trigger" % self._jobName(otdbId), + """ +# notify OTDB +{setStatus_aborted} + +# notify ganglia +wget -O - -q "http://ganglia.control.lofar/ganglia/api/events.php?action=add&start_time=now&summary=Pipeline {obsid} ABORTED&host_regex=" + """ + .format( + setStatus_aborted = setStatus_cmdline("aborted"), + obsid = otdbId, + ), + + sbatch_params=[ + "--partition=%s" % parset.processingPartition(), + "--cpus-per-task=1", + "--ntasks=1", + "--dependency=afternotok:%s" % slurm_job_id, + "--kill-on-invalid-dep=yes", + "--requeue", + "--output=/data/log/abort-trigger-%s.log" % (otdbId,), + ] + ) + logger.info("Scheduled SLURM job %s for abort trigger for otdb_id=%s", slurm_cancel_job_id, otdbId) + + logger.info("Handed over pipeline %s to SLURM, setting status to QUEUED", otdbId) + self._setStatus(otdbId, "queued") + except Exception as e: + logger.error(str(e)) + self._setStatus(otdbId, "aborted") + + def _stopPipeline(self, otdbId): + # Cancel corresponding SLURM job, but first the abort-trigger + # to avoid setting ABORTED as a side effect. + # to be cancelled as well. + + if not self.slurm.isQueuedOrRunning(otdbId): + logger.info("_stopPipeline: Job %s not running", otdbId) + return + + def cancel(jobName): + logger.info("Cancelling job %s", jobName) + self.slurm.cancel(jobName) + + jobName = self._jobName(otdbId) + cancel("%s-abort-trigger" % jobName) + cancel(jobName) + + def _startSuccessors(self, otdbId): + try: + successor_ids = self.dependencies.getSuccessorIds(otdbId) + except PipelineDependencies.TaskNotFoundException, e: + logger.error("_startSuccessors(%s): Error obtaining task successors, not starting them: %s", otdbId, e) + return + + for s in successor_ids: + parset = self._getParset(s) + if not parset or not self._shouldHandle(parset): + continue + + if self.dependencies.canStart(s): + self._startPipeline(s, parset) + else: + logger.info("Job %s still cannot start yet.", otdbId) + + def onObservationScheduled(self, otdbId, modificationTime): + parset = self._getParset(otdbId) + if not parset or not self._shouldHandle(parset): + return + + # Maybe the pipeline can start already + if self.dependencies.canStart(otdbId): + self._startPipeline(otdbId, parset) + else: + logger.info("Job %s was set to scheduled, but cannot start yet.", otdbId) + + def onObservationFinished(self, otdbId, modificationTime): + """ Check if any successors can now start. """ + + logger.info("Considering to start successors of %s", otdbId) + + self._startSuccessors(otdbId) + + def onObservationAborted(self, otdbId, modificationTime): + parset = self._getParset(otdbId) + if parset and not self._shouldHandle(parset): # stop jobs even if there's no parset + return + + logger.info("***** STOP Otdb ID %s *****", otdbId) + self._stopPipeline(otdbId) + + """ + More statusses we want to abort on. + """ + onObservationDescribed = onObservationAborted + onObservationApproved = onObservationAborted + onObservationPrescheduled = onObservationAborted + onObservationConflict = onObservationAborted + onObservationHold = onObservationAborted + diff --git a/MAC/Services/src/pipelinecontrol b/MAC/Services/src/pipelinecontrol new file mode 100644 index 0000000000000000000000000000000000000000..610180e3e5acb07e4607b494267c55a1b7c9f578 --- /dev/null +++ b/MAC/Services/src/pipelinecontrol @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# Copyright (C) 2016 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id: JobsToSchedule.py 33364 2016-01-21 21:21:12Z mol $ + +import logging +from lofar.mac.PipelineControl import PipelineControl +from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_BUSNAME, DEFAULT_OTDB_SERVICE_BUSNAME +from lofar.common.util import waitForInterrupt + +logger = logging.getLogger(__name__) + +if __name__ == "__main__": + import sys + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser("%prog [options]") + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + parser.add_option("-N", "--otdb_notification_bus", dest="otdb_notification_busname", type="string", default=DEFAULT_OTDB_NOTIFICATION_BUSNAME, + help="Bus or queue on which OTDB notifications are received") + parser.add_option("-S", "--otdb_service_bus", dest="otdb_service_busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, + help="Bus or queue to send OTDB requests to") + (options, args) = parser.parse_args() + + if not options.otdb_notification_busname or not options.otdb_service_busname: + parser.print_help() + sys.exit(1) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.DEBUG if options.verbose else logging.INFO) + + with PipelineControl(otdb_notification_busname=options.otdb_notification_busname, otdb_service_busname=options.otdb_service_busname) as pipelineControl: + waitForInterrupt() + diff --git a/MAC/Services/src/pipelinecontrol.ini b/MAC/Services/src/pipelinecontrol.ini new file mode 100644 index 0000000000000000000000000000000000000000..e6bbb791fcc47ac9d88fc9732757c6880fa2f636 --- /dev/null +++ b/MAC/Services/src/pipelinecontrol.ini @@ -0,0 +1,8 @@ +[program:PipelineControl] +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec pipelinecontrol' +user=lofarsys +stopsignal=INT ; KeyboardInterrupt +stopasgroup=true +stdout_logfile=%(program_name)s.log +redirect_stderr=true +stderr_logfile=NONE diff --git a/MAC/Services/test/CMakeLists.txt b/MAC/Services/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b1f0434a8f1707e19fd427b3127dd71721809014 --- /dev/null +++ b/MAC/Services/test/CMakeLists.txt @@ -0,0 +1,8 @@ +# $Id$ + +include(LofarCTest) + +lofar_find_package(Python REQUIRED) + +lofar_add_test(tPipelineControl) + diff --git a/MAC/Services/test/tPipelineControl.py b/MAC/Services/test/tPipelineControl.py new file mode 100644 index 0000000000000000000000000000000000000000..40c6b2dfef6bcfd17b4aeda7f8811c7934c5dfa9 --- /dev/null +++ b/MAC/Services/test/tPipelineControl.py @@ -0,0 +1,378 @@ +#!/usr/bin/env python + +import sys + +from lofar.mac.PipelineControl import * +from lofar.sas.otdb.OTDBBusListener import OTDBBusListener +from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_SUBJECT, DEFAULT_OTDB_SERVICENAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RAS_SERVICENAME +from lofar.messaging import ToBus, Service, EventMessage, MessageHandlerInterface + +from lofar.common.methodtrigger import MethodTrigger + +import subprocess +import unittest +import uuid +import datetime + +import logging +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + +try: + from mock import patch +except ImportError: + print "Cannot run test without python MagicMock" + print "Call 'pip install mock' / 'apt-get install python-mock'" + exit(3) + +def setUpModule(): + pass + +def tearDownModule(): + pass + +class TestRunCommand(unittest.TestCase): + def test_basic(self): + """ Test whether we can run a trivial command. """ + runCommand("true") + + def test_invalid_command(self): + """ Test whether an invalid command produces an error. """ + with self.assertRaises(subprocess.CalledProcessError): + output = runCommand("invalid-command") + + def test_shell(self): + """ Test whether the command is parsed by a shell. """ + runCommand("true --version") + + def test_output(self): + """ Test whether we catch the command output correctly. """ + output = runCommand("echo yes") + self.assertEqual(output, "yes") + + def test_input(self): + """ Test whether we can provide input. """ + output = runCommand("cat -", "yes") + self.assertEqual(output, "yes") + +class TestPipelineControlClassMethods(unittest.TestCase): + def test_shouldHandle(self): + """ Test whether we filter the right OTDB trees. """ + + trials = [ { "type": "Observation", "cluster": "CEP2", "shouldHandle": False }, + { "type": "Observation", "cluster": "CEP4", "shouldHandle": False }, + { "type": "Observation", "cluster": "foo", "shouldHandle": False }, + { "type": "Observation", "cluster": "", "shouldHandle": False }, + { "type": "Pipeline", "cluster": "CEP2", "shouldHandle": False }, + { "type": "Pipeline", "cluster": "CEP4", "shouldHandle": True }, + { "type": "Pipeline", "cluster": "foo", "shouldHandle": True }, + { "type": "Pipeline", "cluster": "", "shouldHandle": False }, + ] + + for t in trials: + parset = { "ObsSW.Observation.processType": t["type"], + "ObsSW.Observation.Cluster.ProcessingCluster.clusterName": t["cluster"] } + self.assertEqual(PipelineControl._shouldHandle(Parset(parset)), t["shouldHandle"]) + +class MockRAService(MessageHandlerInterface): + """ + Fakes RAService calls. + + For each job, radb_id = otdb_id + 1000 to detect misplaced ids. + """ + def __init__(self, predecessors, status): + super(MockRAService, self).__init__() + + self.service2MethodMap = { + "GetTask": self.GetTask, + "GetTasks": self.GetTasks, + } + + self.predecessors = predecessors + self.successors = {x: [s for s in predecessors if x in predecessors[s]] for x in predecessors} + self.status = status + + def GetTask(self, id, mom_id, otdb_id): + print "***** GetTask(%s) *****" % (otdb_id,) + + return { + 'status': self.status[otdb_id], + + 'predecessor_ids': [1000 + x for x in self.predecessors[otdb_id]], + 'successor_ids': [1000 + x for x in self.successors[otdb_id]], + + 'starttime': datetime.datetime.utcnow(), + 'endtime': datetime.datetime.utcnow(), + } + + def GetTasks(self, lower_bound, upper_bound, task_ids, task_status, task_type, mom_ids=None, otdb_ids=None): + print "***** GetTasks(%s) *****" % (task_ids,) + + if task_ids is None: + # Used on startup to check which tasks are at scheduled + return [] + + return [{ + 'otdb_id': t - 1000, + 'status': self.status[t - 1000], + + 'starttime': datetime.datetime.utcnow(), + 'endtime': datetime.datetime.utcnow(), + } for t in task_ids] + +class TestPipelineDependencies(unittest.TestCase): + def setUp(self): + # Create a random bus + self.busname = "%s-%s" % (sys.argv[0], str(uuid.uuid4())[:8]) + self.bus = ToBus(self.busname, { "create": "always", "delete": "always", "node": { "type": "topic" } }) + self.bus.open() + self.addCleanup(self.bus.close) + + # ================================ + # Global state to manipulate + # ================================ + + predecessors = { + 1: [2,3,4], + 2: [3], + 3: [], + 4: [], + } + + status = { + 1: "scheduled", # cannot start, since predecessor 2 hasn't finished + 2: "scheduled", # can start, since predecessor 3 has finished + 3: "finished", + 4: "scheduled", # can start, because no predecessors + } + + # ================================ + # Setup mock ra service + # + # Note that RA IDs are the same as + # OTDB IDs + 1000 in this test. + # ================================ + + service = Service(DEFAULT_RAS_SERVICENAME, + MockRAService, + busname=self.busname, + use_service_methods=True, + handler_args={"predecessors": predecessors, "status": status}) + service.start_listening() + self.addCleanup(service.stop_listening) + + def testGetState(self): + with PipelineDependencies(ra_service_busname=self.busname) as pipelineDependencies: + self.assertEqual(pipelineDependencies.getState(1), "scheduled") + self.assertEqual(pipelineDependencies.getState(2), "scheduled") + self.assertEqual(pipelineDependencies.getState(3), "finished") + self.assertEqual(pipelineDependencies.getState(4), "scheduled") + + def testPredecessorStates(self): + with PipelineDependencies(ra_service_busname=self.busname) as pipelineDependencies: + self.assertEqual(pipelineDependencies.getPredecessorStates(1), {2: "scheduled", 3: "finished", 4: "scheduled"}) + self.assertEqual(pipelineDependencies.getPredecessorStates(3), {}) + + def testSuccessorIds(self): + with PipelineDependencies(ra_service_busname=self.busname) as pipelineDependencies: + self.assertEqual(pipelineDependencies.getSuccessorIds(1), []) + self.assertEqual(pipelineDependencies.getSuccessorIds(3), [1,2]) + + def testCanStart(self): + with PipelineDependencies(ra_service_busname=self.busname) as pipelineDependencies: + self.assertEqual(pipelineDependencies.canStart(1), False) + self.assertEqual(pipelineDependencies.canStart(2), True) + self.assertEqual(pipelineDependencies.canStart(3), False) + self.assertEqual(pipelineDependencies.canStart(4), True) + +class TestPipelineControl(unittest.TestCase): + def setUp(self): + # Create a random bus + self.busname = "%s-%s" % (sys.argv[0], str(uuid.uuid4())[:8]) + self.bus = ToBus(self.busname, { "create": "always", "delete": "always", "node": { "type": "topic" } }) + self.bus.open() + self.addCleanup(self.bus.close) + + # Patch SLURM + class MockSlurm(object): + def __init__(self, *args, **kwargs): + self.scheduled_jobs = {} + + def submit(self, jobName, *args, **kwargs): + print "Schedule SLURM job '%s': %s, %s" % (jobName, args, kwargs) + + self.scheduled_jobs[jobName] = (args, kwargs) + + # Return fake job ID + return "42" + + def isQueuedOrRunning(self, otdbId): + return str(otdbId) in self.scheduled_jobs + + patcher = patch('lofar.mac.PipelineControl.Slurm') + patcher.start().side_effect = MockSlurm + self.addCleanup(patcher.stop) + + # Catch functions to prevent running executables + patcher = patch('lofar.mac.PipelineControl.Parset.dockerImage') + patcher.start().return_value = "lofar-pipeline:trunk" + self.addCleanup(patcher.stop) + + # ================================ + # Global state to manipulate + # ================================ + + predecessors = { + 1: [2,3,4], + 2: [3], + 3: [], + 4: [], + } + + status = { + 1: "prescheduled", + 2: "prescheduled", + 3: "prescheduled", + 4: "prescheduled", + } + + # ================================ + # Setup mock otdb service + # ================================ + + class MockOTDBService(MessageHandlerInterface): + def __init__(self, notification_bus): + """ + notification_bus: bus to send state changes to + """ + super(MockOTDBService, self).__init__() + + self.service2MethodMap = { + "TaskGetSpecification": self.TaskGetSpecification, + "TaskSetStatus": self.TaskSetStatus, + } + + self.notification_bus = notification_bus + + def TaskGetSpecification(self, OtdbID): + print "***** TaskGetSpecification(%s) *****" % (OtdbID,) + + return { "TaskSpecification": { + "Version.number": "1", + PARSET_PREFIX + "Observation.otdbID": str(OtdbID), + PARSET_PREFIX + "Observation.processType": ("Observation" if OtdbID == 4 else "Pipeline"), + PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.clusterName": "CEP4", + PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.clusterPartition": "cpu", + PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.numberOfCoresPerTask": "20", + PARSET_PREFIX + "Observation.Cluster.ProcessingCluster.numberOfTasks": "24", + } } + + def TaskSetStatus(self, OtdbID, NewStatus, UpdateTimestamps): + print "***** TaskSetStatus(%s,%s) *****" % (OtdbID, NewStatus) + + status[OtdbID] = NewStatus + + # Broadcast the state change + content = { "treeID" : OtdbID, "state" : NewStatus, "time_of_change" : datetime.datetime.utcnow() } + msg = EventMessage(context=DEFAULT_OTDB_NOTIFICATION_SUBJECT, content=content) + self.notification_bus.send(msg) + + return {'OtdbID':OtdbID, 'MomID':None, 'Success':True} + + service = Service(DEFAULT_OTDB_SERVICENAME, + MockOTDBService, + busname=self.busname, + use_service_methods=True, + handler_args={ "notification_bus": self.bus }) + service.start_listening() + self.addCleanup(service.stop_listening) + + # ================================ + # Setup mock ra service + # ================================ + + service = Service(DEFAULT_RAS_SERVICENAME, + MockRAService, + busname=self.busname, + use_service_methods=True, + handler_args={"predecessors": predecessors, "status": status}) + service.start_listening() + self.addCleanup(service.stop_listening) + + # ================================ + # Setup listener to catch result + # of our service + # ================================ + + listener = OTDBBusListener(busname=self.busname) + listener.start_listening() + self.addCleanup(listener.stop_listening) + + self.queued_trigger = MethodTrigger(listener, "onObservationQueued") + + def test_setStatus(self): + with PipelineControl(otdb_notification_busname=self.busname, otdb_service_busname=self.busname, ra_service_busname=self.busname) as pipelineControl: + pipelineControl._setStatus(12345, "queued") + + # Wait for the status to propagate + self.assertTrue(self.queued_trigger.wait()) + self.assertEqual(self.queued_trigger.args[0], 12345) + + def testNoPredecessors(self): + """ + Request to start a simulated obsid 3, with the following predecessor tree: + + 3 requires nothing + """ + with PipelineControl(otdb_notification_busname=self.busname, otdb_service_busname=self.busname, ra_service_busname=self.busname) as pipelineControl: + # Send fake status update + pipelineControl._setStatus(3, "scheduled") + + # Wait for pipeline to be queued + self.assertTrue(self.queued_trigger.wait()) + + # Verify message + self.assertEqual(self.queued_trigger.args[0], 3) # otdbId + + # Check if job was scheduled + self.assertIn("3", pipelineControl.slurm.scheduled_jobs) + self.assertIn("3-abort-trigger", pipelineControl.slurm.scheduled_jobs) + + def testPredecessors(self): + """ + Request to start a simulated obsid 1, with the following predecessor tree: + + 1 requires 2, 3, 4 + 2 requires 3 + 4 is an observation + """ + with PipelineControl(otdb_notification_busname=self.busname, otdb_service_busname=self.busname, ra_service_busname=self.busname) as pipelineControl: + # Send fake status update + pipelineControl._setStatus(1, "scheduled") + + # Message should not arrive, as predecessors havent finished + self.assertFalse(self.queued_trigger.wait()) + + # Finish predecessors + pipelineControl._setStatus(2, "finished") + pipelineControl._setStatus(3, "finished") + pipelineControl._setStatus(4, "finished") + + # Wait for pipeline to be queued + self.assertTrue(self.queued_trigger.wait()) + + # Verify message + self.assertEqual(self.queued_trigger.args[0], 1) # otdbId + + # Check if job was scheduled + self.assertIn("1", pipelineControl.slurm.scheduled_jobs) + self.assertIn("1-abort-trigger", pipelineControl.slurm.scheduled_jobs) + +def main(argv): + unittest.main() + +if __name__ == "__main__": + # run all tests + import sys + main(sys.argv[1:]) + diff --git a/MAC/Services/test/tPipelineControl.sh b/MAC/Services/test/tPipelineControl.sh new file mode 100755 index 0000000000000000000000000000000000000000..b209e84048b346972b79287a1691a15fb13ac6f7 --- /dev/null +++ b/MAC/Services/test/tPipelineControl.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runctest.sh tPipelineControl diff --git a/MAC/Tools/Power/ecSetObserving.py b/MAC/Tools/Power/ecSetObserving.py deleted file mode 100755 index 07c6a09c3472c8fee08b022a7dac4a8145790934..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/ecSetObserving.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python -# -# Look for RCUs in ON mode and set EC to observing -# -# 2012 P.Donker - -from isEcLib import * -import time -import subprocess -import os,sys - -def main(): - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - - ec = EC(host) - ec.printInfo(False) - - ec.connectToHost() - time.sleep(1.0) - # version is used to check if function is available in firmware - version,versionstr = ec.getVersion() - ec.disconnectHost() - - # run each minute - while True: - RSPobserving = 0 - TBBobserving = 0 - - response = cmd('rspctl', '--rcu').splitlines() - for line in response: - pos = line.find('RCU[') - if pos != -1: - data = line.strip().split(',')[0].split()[2] - if data == 'ON': - RSPobserving = 1 - - ec.connectToHost() - time.sleep(1.0) - ec.setObserving(RSPobserving) - ec.disconnectHost() - time.sleep(60.0) - -# excecute commandline cmd -def cmd(cmd, args=''): - if len(args) > 0: - proc = subprocess.Popen([cmd, args], stdout=subprocess.PIPE, stderr = subprocess.STDOUT ) - else: - proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (so, se) = proc.communicate() - return(so) - -# start main() -if __name__ == "__main__": - - # Fork the process, so we can run it as a daemon - # using /etc/init.d/ecSetObserving [start/stop/status] - fpid = os.fork() - if fpid!=0: - # Running as daemon now. PID is fpid - sys.exit(0) - - main() - - - diff --git a/MAC/Tools/Power/ecSetObserving.py b/MAC/Tools/Power/ecSetObserving.py new file mode 120000 index 0000000000000000000000000000000000000000..28676a0ccd6af777fd662dbdd418feb1dc8b5e8c --- /dev/null +++ b/MAC/Tools/Power/ecSetObserving.py @@ -0,0 +1 @@ +ec_set_observing.py \ No newline at end of file diff --git a/MAC/Tools/Power/ec_reset_trip.py b/MAC/Tools/Power/ec_reset_trip.py new file mode 100755 index 0000000000000000000000000000000000000000..54110c4cdb252353758937229c9a5b38316ad873 --- /dev/null +++ b/MAC/Tools/Power/ec_reset_trip.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +## Reset trip system in EC unit +## can only be used on LCU +## +## usage: ./ec_reset_trip.py +## +## Author: Pieter Donker (ASTRON) +## Last change: september 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.2.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + ec.resetTrip() + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/ec_set_observing.py b/MAC/Tools/Power/ec_set_observing.py new file mode 100755 index 0000000000000000000000000000000000000000..0916b1dbd17c136ddecd847555722d3a86f601ad --- /dev/null +++ b/MAC/Tools/Power/ec_set_observing.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# +# Look for RCUs in ON mode and set EC to observing +# +# 2016 P.Donker +# +# this script is called by a cron job every minute + +import os +os.chdir('/opt/lofar/sbin/') + +import socket +import time +import subprocess + + +from st_ec_lib import * + +def main(): + host = getIP() + if host == None: + print "===============================================" + print "ERROR, this script can only run on a station" + print "===============================================" + + ec = EC(host) + ec.printInfo(False) + + observing = 0 + + # look for RCU's in ON state + response = cmd('/opt/lofar/bin/rspctl --rcu') + for line in response.splitlines(): + if 'RCU[' in line: + data = line.strip().split(',')[0].split()[-1] + if data == 'ON': + observing = 1 + + #print "set observing %d" % observing + ec.connectToHost() + ec.setObserving(observing) + time.sleep(0.5) + ec.disconnectHost() + +# excecute commandline cmd +def cmd(command): + cmd_list = command.split() + proc = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (so, se) = proc.communicate() + return(so) + +# start main() +if __name__ == "__main__": + main() + + + diff --git a/MAC/Tools/Power/isEcLib.py b/MAC/Tools/Power/isEcLib.py deleted file mode 100755 index 64c847d84f90ddff7aa1fc3fb8c0274f8b582485..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isEcLib.py +++ /dev/null @@ -1,259 +0,0 @@ -## P.Donker ASTRON februari 2011 -## EC IS status module - -import socket -import struct -import time - -def getIP(): - # get ip-adres of LCU - local_host = socket.gethostbyname(socket.gethostname()) - ip = local_host.split('.') - if ip[0] == '10' and ip[1] == '209': - # if LCU adress make it EC adress - return(local_host[:local_host.rfind('.')+1]+'3') - elif ip[0] == '192' and ip[1] == '168': - # if LCU adress make it EC adress - return(local_host[:local_host.rfind('.')+1]+'103') - else: - return(None) - -class EC: - # cmdIDs from TCP PROTOCOL ec-controller - EC_NONE = 0 - EC_STATUS = 1 - EC_CTRL_TEMP = 3 - EC_VERSION = 5 - EC_SET_HEATER = 17 - EC_SET_48 = 20 - EC_RESET_48 = 22 - EC_SET_LCU = 25 - EC_RESET_LCU = 27 - EC_RESET_TRIP = 28 - SET_OBSERVING = 120 - - PWR_OFF = 0 - PWR_ON = 1 - P_48 = 0 - P_LCU = 1 - P_ALL = 2 - - printToScreen = False - host = None - station = None - port = 10000 - sck = None - logger = False - info = '' - version = 0 - versionstr = 'V-.-.-' - - def __init__(self, addr='0.0.0.0'): - self.host = addr - try: - (hostname,a,b) = socket.gethostbyaddr(addr) - self.station = hostname.split('.')[0] - except: - self.station = 'Unknown' - - def setInfo(self, info): - self.info = info - if self.printToScreen: - print self.info - self.info = '' - else: self.info += '\n' - return - - def addInfo(self, info): - self.info += info - if self.printToScreen: - print self.info - self.info = '' - else: self.info += '\n' - return - - def printInfo(self, state=True): - self.printToScreen = state - return - - def hex2bit(self, val=0, bits=16): - bit = '' - for i in range(bits-1,-1,-1): - if val & (1 << i): - bit += '1' - else: - bit += '0' - return(bit) - - #--------------------------------------- - def connectToHost(self): - self.setInfo("connecting to %s on port %d" %(self.host, self.port)) - connected = False - - try: - self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - except socket.error: - self.sck.close() - return(connected) - - try: - self.sck.settimeout(3.0) - self.sck.connect((self.host, self.port)) - connected = True - #time.sleep(0.1) - except socket.error: - self.sck.close() - return(connected) - - #--------------------------------------- - def disconnectHost(self): - self.setInfo("closing %s" %(self.host)) - self.sck.close() - time.sleep(0.5) - return - - #--------------------------------------- - def sendCmd(self, cmdId=0, cab=-1, value=0): - if (cmdId == self.EC_NONE): - return (false) - try: - cmd = struct.pack('hhh', cmdId, cab, int(value)) - self.sck.send(cmd) - except socket.error: - self.setInfo("socket error, try to reconnect") - self.disconnectHost() - time.sleep(10.0) - self.connectToHost() - return - #--------------------------------------- - def recvAck(self): - socketError = False - try: - self.sck.settimeout(1.0) - data = self.sck.recv(6) - except socket.error: - socketError = True - self.setInfo("socket error, try to reconnect") - #self.disconnectHost() - #self.connectToHost() - if socketError: - return(0,0,[]) - - header = struct.unpack('hhh', data) - cmdId = header[0] - status = header[1] - PLSize = header[2] - if (PLSize > 0): - data = self.sck.recv(PLSize) - fmt = 'h' * int(PLSize / 2) - PL = struct.unpack(fmt, data) - else: - PL = [] - return (cmdId, status, PL) - #--------------------------------------- - def setPower(self, pwr=P_ALL, state=PWR_ON): - if ((pwr == self.P_48) or (pwr == self.P_ALL)): - self.sendCmd(self.EC_SET_48, 0, state) - (cmdId, status, PL) = self.recvAck() - self.setInfo('Power Set 48V to %d' %(state)) - if ((pwr == self.P_LCU) or (pwr == self.P_ALL)): - self.sendCmd(self.EC_SET_LCU, 0, state) - (cmdId, status, PL) = self.recvAck() - self.setInfo('Power Set LCU to %d' %(state)) - return(self.info) - #--------------------------------------- - def resetPower(self, pwr=P_ALL): - if ((pwr == self.P_48) or (pwr == self.P_ALL)): - self.sendCmd(self.EC_RESET_48, 0, 0) - (cmdId, status, PL) = self.recvAck() - self.setInfo('PowerReset 48V') - if ((pwr == self.P_LCU) or (pwr == self.P_ALL)): - self.sendCmd(self.EC_RESET_LCU, 0, 0) - (cmdId, status, PL) = self.recvAck() - self.setInfo('PowerReset LCU') - return(self.info) - #--------------------------------------- - def resetTrip(self): - self.sendCmd(self.EC_RESET_TRIP, -1, 0) - (cmdId, status, PL) = self.recvAck() - self.setInfo('Reset Trip System') - return(self.info) - #--------------------------------------- - def setHeater(self, mode=0): - self.sendCmd(self.EC_SET_HEATER, -1, mode) - (cmdId, status, payload) = self.recvAck() - - if (mode == self.MODE_ON): self.setInfo('heater is turned ON') - if (mode == self.MODE_OFF): self.setInfo('heater is turned OFF') - if (mode == self.MODE_AUTO): self.setInfo('heater set to AUTO') - - #--------------------------------------- - def getVersion(self): - self.sendCmd(self.EC_VERSION) - (cmdId, status, PL) = self.recvAck() - try: - version = int((PL[0]*100)+(PL[1]*10)+PL[2]) - versionstr = 'V%d.%d.%d' %(PL) - self.version = version - self.versionstr = versionstr - self.setInfo('EC software version %d.%d.%d' %(PL)) - except: - version = 0 - versionstr = 'V0.0.0' - self.version = version - self.versionstr = versionstr - self.setInfo('EC software version 0.0.0') - - return version, versionstr - - #--------------------------------------- - def getStatusData(self): - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL2) = self.recvAck() - return PL2 - - def getStatus(self): - onoff = ('OFF','ON') - badok = ('N.A.','OK') - - # get information from EC - self.sendCmd(self.EC_CTRL_TEMP) - (cmdId, status, PL1) = self.recvAck() - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL2) = self.recvAck() - if len(PL1) == 0 or len(PL2) == 0: return - # fill lines with data - lines = [] - lines.append('temperature = %5.2f' %(PL2[2]/100.)) - lines.append('humidity = %5.2f' %(PL2[3]/100.)) - lines.append('heater state = %s' %(onoff[PL2[(3*7)+6]])) - lines.append('power 48V state = %s' %(onoff[(PL2[28] & 1)])) - lines.append('power LCU state = %s' %(onoff[(PL2[28] >> 1) & 1])) - lines.append('lightning state = %s' %(badok[(PL2[29] & 1)])) - - # print lines to screen or file, see printInfo - info1 = ' %s (EC %s)' %(self.station, self.versionstr) - info2 = ' %s ' %(time.asctime()) - self.setInfo('-' * len(info2)) - self.addInfo(info1) - self.addInfo(info2) - self.addInfo('-' * len(info2)) - for line in lines: - self.addInfo(line) - - #--------------------------------------- - def getPowerStatus(self): - state = ('OFF','ON') - # get information from EC - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL) = self.recvAck() - - self.addInfo('Power: 48V = %s, LCU = %s' %(state[(PL[28] & 1)], state[(PL[28] >> 1)])) - - #--------------------------------------- - ## set observing active(1) or not active(0) - def setObserving(self, state=0): - self.sendCmd(self.SET_OBSERVING, -1, state) - (cmdId, status, PL) = self.recvAck() - self.setInfo('SetObserving to %d' %(state)) - return(self.info) diff --git a/MAC/Tools/Power/isEcLib.py b/MAC/Tools/Power/isEcLib.py new file mode 120000 index 0000000000000000000000000000000000000000..1e65dd3e22300b482db5083928962a729e4612a3 --- /dev/null +++ b/MAC/Tools/Power/isEcLib.py @@ -0,0 +1 @@ +st_ec_lib.py \ No newline at end of file diff --git a/MAC/Tools/Power/isReset48V.py b/MAC/Tools/Power/isReset48V.py deleted file mode 100755 index 6a2cb38d66a9bd5ece849f12d968f705c9a6c6c4..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isReset48V.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python - -## "isReset48V.py" -## RESET 48V powersupply on IS (international station) -## can only be used on IS (international) LCU -## -## usage: ./isReset48V.py -## -## Author: Pieter Donker (ASTRON) -## Last change: november 2011 - -from isEcLib import * -import sys -import time - -VERSION = '1.1.0' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - ec.resetPower(ec.P_48) - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isReset48V.py b/MAC/Tools/Power/isReset48V.py new file mode 120000 index 0000000000000000000000000000000000000000..6987f263f1747d45ea0bdb1f53a67137b387ca8b --- /dev/null +++ b/MAC/Tools/Power/isReset48V.py @@ -0,0 +1 @@ +reset_48v.py \ No newline at end of file diff --git a/MAC/Tools/Power/isResetLCU.py b/MAC/Tools/Power/isResetLCU.py deleted file mode 100755 index 46e7c73270081a5b58ae4219b40a51a87a844959..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isResetLCU.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python - -## "isResetLCU.py" -## RESET LCU power on IS (international station) -## can only be used on IS LCU -## -## USE ONLY IF NORMAL SHUTDOWN IS NOT POSSIBLE -## usage: ./isResetLCU.py -## -## Author: Pieter Donker (ASTRON) -## Last change: november 2011 - -from isEcLib import * -import sys -import time - -VERSION = '1.1.0' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - print "Turn off the mains voltage for 10 seconds" - print "Use only if normal shutdown in not possible" - if 'yes' == raw_input("Do you realy want to cycle LCU power [yes/no] : "): - print - print "================================" - print " cycle LCU power in 5 seconds " - print "================================" - time.sleep(5.0) - ec.resetPower(ec.P_LCU) - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isResetLCU.py b/MAC/Tools/Power/isResetLCU.py new file mode 120000 index 0000000000000000000000000000000000000000..729a66dac82f14b04044482e4ad9d1d89635ab1f --- /dev/null +++ b/MAC/Tools/Power/isResetLCU.py @@ -0,0 +1 @@ +reset_lcu.py \ No newline at end of file diff --git a/MAC/Tools/Power/isResetTrip.py b/MAC/Tools/Power/isResetTrip.py deleted file mode 100755 index dd11302771e688e274d102959243929e6c75ecc6..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isResetTrip.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -## "isReset48V.py" -## RESET 48V powersupply on IS (international station) -## can only be used on IS (international) LCU -## -## usage: ./isReset48V.py -## -## Author: Pieter Donker (ASTRON) -## Last change: november 2011 - -from isEcLib import * -import sys -import time - -VERSION = '1.1.0' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - ec.resetTrip() - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isResetTrip.py b/MAC/Tools/Power/isResetTrip.py new file mode 120000 index 0000000000000000000000000000000000000000..ca55cad4cda3abf7710b210917e1f405c5c021d8 --- /dev/null +++ b/MAC/Tools/Power/isResetTrip.py @@ -0,0 +1 @@ +ec_reset_trip.py \ No newline at end of file diff --git a/MAC/Tools/Power/isStatus.py b/MAC/Tools/Power/isStatus.py deleted file mode 100755 index 574b72f5647582bd83ecef57bf92ce8951ebb518..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isStatus.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python - -## "isStatus.py" -## Print EC status of IS (international station) -## can only be used on IS LCU -## -## usage: ./isStatus.py -## -## Author: Pieter Donker (ASTRON) -## Last change: november 2011 - -from isEcLib import * -import sys -import time - -VERSION = '1.1.0' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - # version is used to check if function is available in firmware - version,versionstr = ec.getVersion() - ec.printInfo(True) - ec.getStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isStatus.py b/MAC/Tools/Power/isStatus.py new file mode 120000 index 0000000000000000000000000000000000000000..65773e96ec556c207177646efcb09b94323e653b --- /dev/null +++ b/MAC/Tools/Power/isStatus.py @@ -0,0 +1 @@ +status.py \ No newline at end of file diff --git a/MAC/Tools/Power/isStatusData.py b/MAC/Tools/Power/isStatusData.py deleted file mode 100755 index 7d3f474448dd6d56da101f8455a6cfb6a88e5cf8..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isStatusData.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python - -""" -write status-data to stdout -stdout format: - time [0] data_cab0 [1] data_cab1 [3] data_cab3 - -values in data_cabx: - temperature humidity fansstate heaterstate - temperature : actual temperature in cabinet - humidity : actual humidity in cabinet - fanstate : which fans are on - bit 0 outer fan front - bit 1 inner fan front - bit 2 inner fan back - bit 4 outer fan back - heaterstate : only available in cabinet 3 - 0 = off - 1 = on - -example, returned data: -1333702601 [0] 24.71 16.81 4 0 [1] 24.72 43.36 4 0 [3] 14.69 41.73 2 0 - -""" - -#from eccontrol import * -#from stations import * -import isEcLib as eclib -import sys -import time - -VERSION = '1.0.0' # version of this script -Station = '' - -##======================================================================= -## start of main program -##======================================================================= -def main(): - ec = eclib.EC(eclib.getIP()) - ec.connectToHost() - ec.printInfo(False) # print NOT to screen - - # version is used to check if function is available in firmware - PL2 = ec.getStatusData() - print '%1.0f' %(time.time()), - cabs = [3] - for cab in cabs: - # print cabnr, temperature, humidity, fansstate, heaterstate - print '[%d] %1.2f %1.2f %d %d' %\ - ( cab, PL2[(cab*7)+2]/100., PL2[(cab*7)+3]/100., - PL2[(cab*7)+4] & 0x0f, PL2[(cab*7)+6]), - print - - ##---------------------------------------------------------------------- - ## do not delete next lines - ec.disconnectHost() - - -if __name__ == '__main__': - main() - diff --git a/MAC/Tools/Power/isStatusData.py b/MAC/Tools/Power/isStatusData.py new file mode 120000 index 0000000000000000000000000000000000000000..95d9f7941c0944d9b137977489417542d680b0c5 --- /dev/null +++ b/MAC/Tools/Power/isStatusData.py @@ -0,0 +1 @@ +status_data.py \ No newline at end of file diff --git a/MAC/Tools/Power/isTurnOff48V.py b/MAC/Tools/Power/isTurnOff48V.py deleted file mode 100755 index bf28be0a5b5f4ed9f07b31182f85d941f225946b..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isTurnOff48V.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -## "isOff48V.py" -## Turn off 48V powersupply on IS (international station) -## can only be used on IS (international) LCU -## -## usage: ./isOff48V.py -## -## Author: Pieter Donker (ASTRON) -## Last change: May 2013 - -from isEcLib import * -import sys -import time - -VERSION = '0.0.1' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - ec.setPower(ec.P_48, ec.PWR_OFF) - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isTurnOff48V.py b/MAC/Tools/Power/isTurnOff48V.py new file mode 120000 index 0000000000000000000000000000000000000000..26117b17a3f51cf8a76ec5a4a6dbd1f60bc1f292 --- /dev/null +++ b/MAC/Tools/Power/isTurnOff48V.py @@ -0,0 +1 @@ +turn_off_48v.py \ No newline at end of file diff --git a/MAC/Tools/Power/isTurnOffLCU.py b/MAC/Tools/Power/isTurnOffLCU.py deleted file mode 100755 index b8f67bbeb27d7c269212419c98613bef57094d77..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isTurnOffLCU.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -## "isOff48V.py" -## Turn off 48V powersupply on IS (international station) -## can only be used on IS (international) LCU -## -## usage: ./isOff48V.py -## -## Author: Pieter Donker (ASTRON) -## Last change: May 2013 - -from isEcLib import * -import sys -import time - -VERSION = '0.0.1' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - ec.setPower(ec.P_LCU, ec.PWR_OFF) - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isTurnOffLCU.py b/MAC/Tools/Power/isTurnOffLCU.py new file mode 120000 index 0000000000000000000000000000000000000000..11caae5ac99ba6c41eaef4887ab42f64cdf4a2b2 --- /dev/null +++ b/MAC/Tools/Power/isTurnOffLCU.py @@ -0,0 +1 @@ +turn_off_lcu.py \ No newline at end of file diff --git a/MAC/Tools/Power/isTurnOn48V.py b/MAC/Tools/Power/isTurnOn48V.py deleted file mode 100755 index 91216df6ae54dc69c5441d2ffb47aad09c2dca0f..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/isTurnOn48V.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -## "isOff48V.py" -## Turn off 48V powersupply on IS (international station) -## can only be used on IS (international) LCU -## -## usage: ./isOff48V.py -## -## Author: Pieter Donker (ASTRON) -## Last change: May 2013 - -from isEcLib import * -import sys -import time - -VERSION = '0.0.1' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - ec.printInfo(True) - ec.getPowerStatus() - ec.setPower(ec.P_48, ec.PWR_ON) - print "waiting 10 seconds" - time.sleep(10.0) - ec.getPowerStatus() - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/isTurnOn48V.py b/MAC/Tools/Power/isTurnOn48V.py new file mode 120000 index 0000000000000000000000000000000000000000..b8c3c7c03fd5521f8d0e8eea26836194c8d1eb69 --- /dev/null +++ b/MAC/Tools/Power/isTurnOn48V.py @@ -0,0 +1 @@ +turn_on_48v.py \ No newline at end of file diff --git a/MAC/Tools/Power/nlEcLib.py b/MAC/Tools/Power/nlEcLib.py deleted file mode 100755 index 33fcf0d2f4cfa8d8b5ae10883b428e33cb39063a..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/nlEcLib.py +++ /dev/null @@ -1,269 +0,0 @@ -## P.Donker ASTRON februari 2011 -## EC NL status module - -import socket -import struct -import time - -def getIP(): - # get ip-adres of LCU - local_host = socket.gethostbyname(socket.gethostname()) - ip = local_host.split('.') - if ip[0] == '10' and ip[1] == '151': - # if LCU adress make it EC adress - return(local_host[:local_host.rfind('.')+1]+'3') - return(None) - -class EC: - # cmdIDs from TCP PROTOCOL ec-controller - EC_NONE = 0 - EC_STATUS = 1 - EC_CTRL_TEMP = 3 - EC_VERSION = 5 - EC_STATION_INFO = 6 - - printToScreen = False - host = None - station = None - port = 10000 - sck = None - logger = False - info = '' - version = 0 - versionstr = 'V-.-.-' - - def __init__(self, addr='0.0.0.0'): - self.host = addr - try: - (hostname,a,b) = socket.gethostbyaddr(addr) - self.station = hostname.split('.')[0] - except: - self.station = 'Unknown' - - def setInfo(self, info): - self.info = info - if self.printToScreen: - print self.info - self.info = '' - else: self.info += '\n' - return - - def addInfo(self, info): - self.info += info - if self.printToScreen: - print self.info - self.info = '' - else: self.info += '\n' - return - - def printInfo(self, state=True): - self.printToScreen = state - return - - def hex2bit(self, val=0, bits=16): - bit = '' - for i in range(bits-1,-1,-1): - if val & (1 << i): - bit += '1' - else: - bit += '0' - return(bit) - - #--------------------------------------- - def connectToHost(self): - self.setInfo("connecting to %s on port %d" %(self.host, self.port)) - connected = False - - try: - self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - except socket.error: - self.sck.close() - return(connected) - - try: - self.sck.settimeout(3.0) - self.sck.connect((self.host, self.port)) - connected = True - #time.sleep(0.1) - except socket.error: - self.sck.close() - return(connected) - - #--------------------------------------- - def disconnectHost(self): - self.setInfo("closing %s" %(self.host)) - self.sck.close() - time.sleep(1.0) - return - - #--------------------------------------- - def sendCmd(self, cmdId=0, cab=-1, value=0): - if (cmdId == self.EC_NONE): - return (false) - try: - cmd = struct.pack('hhh', cmdId, cab, int(value)) - self.sck.send(cmd) - except socket.error: - self.setInfo("socket error, try to reconnect") - self.disconnectHost() - time.sleep(10.0) - self.connectToHost() - return - #--------------------------------------- - def recvAck(self): - socketError = False - try: - self.sck.settimeout(1.0) - data = self.sck.recv(6) - except socket.error: - socketError = True - self.setInfo("socket error, try to reconnect") - #self.disconnectHost() - #self.connectToHost() - if socketError: - return(0,0,[]) - - header = struct.unpack('hhh', data) - cmdId = header[0] - status = header[1] - PLSize = header[2] - if (PLSize > 0): - data = self.sck.recv(PLSize) - fmt = 'h' * int(PLSize / 2) - PL = struct.unpack(fmt, data) - else: - PL = [] - return (cmdId, status, PL) - - #--------------------------------------- - def getVersion(self): - self.sendCmd(self.EC_VERSION) - (cmdId, status, PL) = self.recvAck() - try: - version = int((PL[0]*100)+(PL[1]*10)+PL[2]) - versionstr = 'V%d.%d.%d' %(PL) - self.version = version - self.versionstr = versionstr - self.setInfo('EC software version %d.%d.%d' %(PL)) - except: - version = 0 - versionstr = 'V0.0.0' - self.version = version - self.versionstr = versionstr - self.setInfo('EC software version 0.0.0') - - return version, versionstr - - def getStationInfo(self): - stationType = ('Unknown','NAA','Unknown','LOFAR NL','LOFAR IS','Unknown') - wxt520Text = ('not available','available') - self.sendCmd(self.EC_STATION_INFO) - (cmdId, status, PL) = self.recvAck() - - type = int(PL[0]) - wxt520 = int(PL[1]) - self.stationtype = type - self.wxt520present = wxt520 - self.setInfo('station type: %s, wxt520: %s' %\ - (stationType[type], wxt520Text[wxt520])) - return type, wxt520 - #--------------------------------------- - def getStatusData(self): - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL2) = self.recvAck() - return PL2 - - def getStatus(self): - ec_mode = ('OFF','ON','AUTO','MANUAL','STARTUP','AUTO-SEEK','ABSENT','TEST') - fan = ('. . . .','. . . .','. 2 . .','1 2 . .',\ - '. . 3 .','. . . .','. 2 3 .','1 2 3 .',\ - '. . . .','. . . .','. . . .','. . . .',\ - '. . 3 4','. . . .','. 2 3 4','1 2 3 4') - - door = ('CLOSED','FRONT_OPEN','BACK_OPEN','ALL_OPEN') - fanstate = ('BAD | BAD ','GOOD| BAD ','BAD | GOOD','GOOD| GOOD') - fanestate= ('OFF | OFF ','ON | OFF ','OFF | ON ','ON | ON ') - onoff = ('OFF','ON') - badok = ('N.A.','OK') - - # get information from EC - self.sendCmd(self.EC_CTRL_TEMP) - (cmdId, status, PL1) = self.recvAck() - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL2) = self.recvAck() - if len(PL1) == 0 or len(PL2) == 0: return - # fill lines with data - lines = [] - - lines.append(' |') - lines.append('mode |') - lines.append('status |') - lines.append('set point |') - lines.append('temperature |') - lines.append('humidity |') - lines.append('fans |') - lines.append('fane |') - lines.append('fans state |') - lines.append('doors |') - lines.append('heater |') - cabs = [0,1,3] - for nCab in range(3): - cab = cabs[nCab] - lines[0] += ' cabinet %1d |' %(cab) - lines[1] += '%11s |' %(ec_mode[PL2[(cab*7)]]) - lines[2] += ' %#06x |' %(PL2[(cab*7)+1]) - lines[3] += '%11.2f |' %(PL1[cab]/100.) - lines[4] += '%11.2f |' %(PL2[(cab*7)+2]/100.) - lines[5] += '%11.2f |' %(PL2[(cab*7)+3]/100.) - lines[6] += '%11s |' %(fan[(PL2[(cab*7)+4]&0x0f)]) - lines[7] += '%11s |' %(fanestate[(PL2[(cab*7)+4]>>4)&0x3]) - lines[8] += '%11s |' %(fanstate[(PL2[(cab*7)+4]>>6)&0x3]) - lines[9] += '%11s |' %(door[(PL2[(cab*7)+5]&0x03)]) - if (cab != 3): - lines[10] += '%11s |' %('none') - else: - lines[10] += '%11s |' %(onoff[PL2[(cab*7)+6]]) - - i = 28 - lines.append('power 48V state = %s' %(onoff[(PL2[i] & 1)])) - lines.append('power LCU state = %s' %(onoff[(PL2[i] >> 1)])) - lines.append('lightning state = %s' %(badok[(PL2[i+1] & 1)])) - - # print lines to screen or file, see printInfo - info = 'status %s (%s) %s ' %(self.station, self.versionstr, time.asctime()) - self.setInfo('-' * len(info)) - self.addInfo(info) - self.addInfo('-' * len(info)) - - for line in lines: - self.addInfo(line) - - #--------------------------------------- - def getTripStatus(self): - # get information from EC - self.sendCmd(self.EC_STATUS) - (cmdId, status, PL) = self.recvAck() - state = False - if (PL[1] & 0x1000): - self.addInfo('trip in cabinet 0') - state = True - if (PL[8] & 0x1000): - self.addInfo('trip in cabinet 1') - state = True - if (PL[22] & 0x1000): - self.addInfo('trip in cabinet 3') - state = True - - if (PL[1] & 0x6000): - self.addInfo('warning in cabinet 0') - state = True - if (PL[8] & 0x6000): - self.addInfo('warning in cabinet 1') - state = True - if (PL[22] & 0x6000): - self.addInfo('warning in cabinet 3') - state = True - - if (state == False): - self.addInfo('NO trips available') - return(state) diff --git a/MAC/Tools/Power/nlEcLib.py b/MAC/Tools/Power/nlEcLib.py new file mode 120000 index 0000000000000000000000000000000000000000..1e65dd3e22300b482db5083928962a729e4612a3 --- /dev/null +++ b/MAC/Tools/Power/nlEcLib.py @@ -0,0 +1 @@ +st_ec_lib.py \ No newline at end of file diff --git a/MAC/Tools/Power/nlStatus.py b/MAC/Tools/Power/nlStatus.py deleted file mode 100755 index 0e51c2006b49501e4fcfc9761469eaf66bb72774..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/nlStatus.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python - -## "nlStatus.py" -## Print EC status of NL(dutch) station -## can only be used on NL (dutch) LCU -## -## usage: ./nlStatus.py -## -## Author: Pieter Donker (ASTRON) -## Last change: november 2011 - -from nlEcLib import * -import sys -import time - -VERSION = '1.1.0' # version of this script - -# used variables -version = 0 # EC version -versionstr = 'V-.-.-' - -##======================================================================= -## start of main program -##======================================================================= -if __name__ == '__main__': - host = getIP() - #print host - if host == None: - print "===============================================" - print "ERROR, this script can only run on a NL station" - print "===============================================" - else: - ec = EC(host) - ec.connectToHost() - time.sleep(1.0) - # version is used to check if function is available in firmware - version,versionstr = ec.getVersion() - ec.printInfo(True) - print "" - ec.getStationInfo() - ec.getStatus() - print "" - ec.getTripStatus() - print "" - ec.printInfo(False) - ec.disconnectHost() - - diff --git a/MAC/Tools/Power/nlStatus.py b/MAC/Tools/Power/nlStatus.py new file mode 120000 index 0000000000000000000000000000000000000000..65773e96ec556c207177646efcb09b94323e653b --- /dev/null +++ b/MAC/Tools/Power/nlStatus.py @@ -0,0 +1 @@ +status.py \ No newline at end of file diff --git a/MAC/Tools/Power/nlStatusData.py b/MAC/Tools/Power/nlStatusData.py deleted file mode 100755 index dbd6a302ea7ca91b9b84a19ee553a575df028d22..0000000000000000000000000000000000000000 --- a/MAC/Tools/Power/nlStatusData.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python - -""" -write status-data to stdout -stdout format: - time [0] data_cab0 [1] data_cab1 [3] data_cab3 - -values in data_cabx: - temperature humidity fansstate heaterstate - temperature : actual temperature in cabinet - humidity : actual humidity in cabinet - fanstate : which fans are on - bit 0 outer fan front - bit 1 inner fan front - bit 2 inner fan back - bit 4 outer fan back - heaterstate : only available in cabinet 3 - 0 = off - 1 = on - -example, returned data: -1333702601 [0] 24.71 16.81 4 0 [1] 24.72 43.36 4 0 [3] 14.69 41.73 2 0 - -""" - -#from eccontrol import * -#from stations import * -import nlEcLib as eclib -import sys -import time - -VERSION = '1.0.0' # version of this script -Station = '' - -##======================================================================= -## start of main program -##======================================================================= -def main(): - ec = eclib.EC(eclib.getIP()) - ec.connectToHost() - ec.printInfo(False) # print NOT to screen - - # version is used to check if function is available in firmware - PL2 = ec.getStatusData() - print '%1.0f' %(time.time()), - cabs = [0,1,3] - for cab in cabs: - # print cabnr, temperature, humidity, fansstate, heaterstate - print '[%d] %1.2f %1.2f %d %d' %\ - ( cab, PL2[(cab*7)+2]/100., PL2[(cab*7)+3]/100., - PL2[(cab*7)+4] & 0x0f, PL2[(cab*7)+6]), - print - - ##---------------------------------------------------------------------- - ## do not delete next lines - ec.disconnectHost() - - -if __name__ == '__main__': - main() - diff --git a/MAC/Tools/Power/nlStatusData.py b/MAC/Tools/Power/nlStatusData.py new file mode 120000 index 0000000000000000000000000000000000000000..95d9f7941c0944d9b137977489417542d680b0c5 --- /dev/null +++ b/MAC/Tools/Power/nlStatusData.py @@ -0,0 +1 @@ +status_data.py \ No newline at end of file diff --git a/MAC/Tools/Power/reset_48v.py b/MAC/Tools/Power/reset_48v.py new file mode 100644 index 0000000000000000000000000000000000000000..4e050945dc89f272848d6689273a27453973e6bb --- /dev/null +++ b/MAC/Tools/Power/reset_48v.py @@ -0,0 +1,46 @@ +#!/usr/bin/python + +## RESET 48V powersupply +## can only be used on LCU +## +## usage: ./reset_48v.py +## +## Author: Pieter Donker (ASTRON) +## Last change: september 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.2.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + ec.resetPower(ec.P_48) + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/reset_lcu.py b/MAC/Tools/Power/reset_lcu.py new file mode 100755 index 0000000000000000000000000000000000000000..138d57f223c07a1549d738f00e38b1a471a6803b --- /dev/null +++ b/MAC/Tools/Power/reset_lcu.py @@ -0,0 +1,55 @@ +#!/usr/bin/python + +## RESET LCU power +## can only be used on LCU +## +## USE ONLY IF NORMAL SHUTDOWN IS NOT POSSIBLE +## usage: ./reset_lcu.py +## +## Author: Pieter Donker (ASTRON) +## Last change: september 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.2.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "===============================================" + print "ERROR, this script can only run on a station" + print "===============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + print "Turn off the mains voltage for 10 seconds" + print "Use only if normal shutdown in not possible" + if 'yes' == raw_input("Do you realy want to cycle LCU power [yes/no] : "): + print + print "================================" + print " cycle LCU power in 5 seconds " + print "================================" + time.sleep(5.0) + ec.resetPower(ec.P_LCU) + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/st_ec_lib.py b/MAC/Tools/Power/st_ec_lib.py new file mode 100755 index 0000000000000000000000000000000000000000..a900e915e0d7f0019164ebf9c8892a574c99bf29 --- /dev/null +++ b/MAC/Tools/Power/st_ec_lib.py @@ -0,0 +1,368 @@ +## P.Donker ASTRON februari 2011 +## station EC lib module + +import socket +import struct +import time + +def getIP(): + # get ip-adres of LCU + local_host = socket.gethostbyname(socket.gethostname()) + ip = local_host.split('.') + if ip[0] == '10' and (ip[1] == '151' or ip[1] == '209'): + # if LCU adress make it EC adress + return(local_host[:local_host.rfind('.')+1]+'3') + return(None) + +class EC: + # cmdIDs from TCP PROTOCOL ec-controller + EC_NONE = 0 + EC_STATUS = 1 + EC_CTRL_TEMP = 3 + EC_VERSION = 5 + EC_STATION_INFO = 6 + EC_SET_HEATER = 17 + EC_SET_48 = 20 + EC_RESET_48 = 22 + EC_SET_LCU = 25 + EC_RESET_LCU = 27 + EC_RESET_TRIP = 28 + SET_OBSERVING = 120 + + PWR_OFF = 0 + PWR_ON = 1 + P_48 = 0 + P_LCU = 1 + P_ALL = 2 + + printToScreen = False + host = None + station = None + port = 10000 + sck = None + logger = False + info = '' + version = 0 + versionstr = 'V-.-.-' + + def __init__(self, addr='0.0.0.0'): + self.host = addr + try: + (hostname,a,b) = socket.gethostbyaddr(addr) + self.station = hostname.split('.')[0] + except: + self.station = 'Unknown' + + def setInfo(self, info): + self.info = info + if self.printToScreen: + print self.info + self.info = '' + else: self.info += '\n' + return + + def addInfo(self, info): + self.info += info + if self.printToScreen: + print self.info + self.info = '' + else: self.info += '\n' + return + + def printInfo(self, state=True): + self.printToScreen = state + return + + #--------------------------------------- + def connectToHost(self): + self.setInfo("connecting to %s on port %d" %(self.host, self.port)) + connected = False + + try: + self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except socket.error: + self.sck.close() + return(connected) + + try: + self.sck.settimeout(3.0) + self.sck.connect((self.host, self.port)) + connected = True + time.sleep(0.5) + self.getVersion() + if self.version > 200: + self.getStationInfo() + except socket.error: + self.sck.close() + return(connected) + + #--------------------------------------- + def disconnectHost(self): + self.setInfo("closing %s" %(self.host)) + self.sck.close() + time.sleep(0.5) + return + + #--------------------------------------- + def sendCmd(self, cmdId=0, cab=-1, value=0): + if (cmdId == self.EC_NONE): + return (false) + try: + cmd = struct.pack('hhh', cmdId, cab, int(value)) + self.sck.send(cmd) + except socket.error: + self.setInfo("socket error, try to reconnect") + self.disconnectHost() + time.sleep(10.0) + self.connectToHost() + return + #--------------------------------------- + def recvAck(self): + socketError = False + try: + self.sck.settimeout(1.0) + data = self.sck.recv(6) + except socket.error: + socketError = True + self.setInfo("socket error, try to reconnect") + #self.disconnectHost() + #self.connectToHost() + if socketError: + return(0,0,[]) + + header = struct.unpack('hhh', data) + cmdId = header[0] + status = header[1] + PLSize = header[2] + if (PLSize > 0): + data = self.sck.recv(PLSize) + fmt = 'h' * int(PLSize / 2) + PL = struct.unpack(fmt, data) + else: + PL = [] + return (cmdId, status, PL) + #--------------------------------------- + def setPower(self, pwr=P_ALL, state=PWR_ON): + if ((pwr == self.P_48) or (pwr == self.P_ALL)): + self.sendCmd(self.EC_SET_48, 0, state) + (cmdId, status, PL) = self.recvAck() + self.setInfo('Power Set 48V to %d' %(state)) + if ((pwr == self.P_LCU) or (pwr == self.P_ALL)): + self.sendCmd(self.EC_SET_LCU, 0, state) + (cmdId, status, PL) = self.recvAck() + self.setInfo('Power Set LCU to %d' %(state)) + return(self.info) + #--------------------------------------- + def resetPower(self, pwr=P_ALL): + if ((pwr == self.P_48) or (pwr == self.P_ALL)): + self.sendCmd(self.EC_RESET_48, 0, 0) + (cmdId, status, PL) = self.recvAck() + self.setInfo('PowerReset 48V') + if ((pwr == self.P_LCU) or (pwr == self.P_ALL)): + self.sendCmd(self.EC_RESET_LCU, 0, 0) + (cmdId, status, PL) = self.recvAck() + self.setInfo('PowerReset LCU') + return(self.info) + #--------------------------------------- + def resetTrip(self): + self.sendCmd(self.EC_RESET_TRIP, -1, 0) + (cmdId, status, PL) = self.recvAck() + self.setInfo('Reset Trip System') + return(self.info) + #--------------------------------------- + def setHeater(self, mode=0): + self.sendCmd(self.EC_SET_HEATER, -1, mode) + (cmdId, status, payload) = self.recvAck() + + if (mode == self.MODE_ON): self.setInfo('heater is turned ON') + if (mode == self.MODE_OFF): self.setInfo('heater is turned OFF') + if (mode == self.MODE_AUTO): self.setInfo('heater set to AUTO') + + #--------------------------------------- + def getVersion(self): + self.sendCmd(self.EC_VERSION) + (cmdId, status, PL) = self.recvAck() + try: + version = int((PL[0]*100)+(PL[1]*10)+PL[2]) + versionstr = 'V%d.%d.%d' %(PL) + self.version = version + self.versionstr = versionstr + self.setInfo('EC software version %d.%d.%d' %(PL)) + except: + version = 0 + versionstr = 'V0.0.0' + self.version = version + self.versionstr = versionstr + self.setInfo('EC software version 0.0.0') + + return version, versionstr + + def getStationInfo(self): + stationType = ('Unknown','NAA','Unknown','LOFAR NL','LOFAR IS','Unknown') + wxt520Text = ('not available','available') + self.sendCmd(self.EC_STATION_INFO) + (cmdId, status, PL) = self.recvAck() + + type = int(PL[0]) + wxt520 = int(PL[1]) + self.stationtype = type + self.wxt520present = wxt520 + self.setInfo('station type: %s, wxt520: %s' %\ + (stationType[type], wxt520Text[wxt520])) + return type, wxt520 + + #--------------------------------------- + def getStatusData(self): + self.sendCmd(self.EC_STATUS) + (cmdId, status, PL2) = self.recvAck() + return PL2 + + def getStatus(self): + if self.stationtype == 3: + self.statusNL() + return() + if self.stationtype == 4: + self.statusIS() + return() + self.setInfo("Unsupported station type") + + def statusNL(self): + ec_mode = ('OFF','ON','AUTO','MANUAL','STARTUP','AUTO-SEEK','ABSENT','TEST') + fan = ('. . . .','. . . .','. 2 . .','1 2 . .',\ + '. . 3 .','. . . .','. 2 3 .','1 2 3 .',\ + '. . . .','. . . .','. . . .','. . . .',\ + '. . 3 4','. . . .','. 2 3 4','1 2 3 4') + + door = ('CLOSED','FRONT_OPEN','BACK_OPEN','ALL_OPEN') + fanstate = ('BAD | BAD ','GOOD| BAD ','BAD | GOOD','GOOD| GOOD') + fanestate= ('OFF | OFF ','ON | OFF ','OFF | ON ','ON | ON ') + onoff = ('OFF','ON') + badok = ('N.A.','OK') + + # get information from EC + self.sendCmd(self.EC_CTRL_TEMP) + (cmdId, status, PL1) = self.recvAck() + self.sendCmd(self.EC_STATUS) + (cmdId, status, PL2) = self.recvAck() + if len(PL1) == 0 or len(PL2) == 0: return + # fill lines with data + lines = [] + + lines.append(' |') + lines.append('mode |') + lines.append('status |') + lines.append('set point |') + lines.append('temperature |') + lines.append('humidity |') + lines.append('fans |') + lines.append('fane |') + lines.append('fans state |') + lines.append('doors |') + lines.append('heater |') + cabs = [0,1,3] + for nCab in range(3): + cab = cabs[nCab] + lines[0] += ' cabinet %1d |' %(cab) + lines[1] += '%11s |' %(ec_mode[PL2[(cab*7)]]) + lines[2] += ' %#06x |' %(PL2[(cab*7)+1]) + lines[3] += '%11.2f |' %(PL1[cab]/100.) + lines[4] += '%11.2f |' %(PL2[(cab*7)+2]/100.) + lines[5] += '%11.2f |' %(PL2[(cab*7)+3]/100.) + lines[6] += '%11s |' %(fan[(PL2[(cab*7)+4]&0x0f)]) + lines[7] += '%11s |' %(fanestate[(PL2[(cab*7)+4]>>4)&0x3]) + lines[8] += '%11s |' %(fanstate[(PL2[(cab*7)+4]>>6)&0x3]) + lines[9] += '%11s |' %(door[(PL2[(cab*7)+5]&0x03)]) + if (cab != 3): + lines[10] += '%11s |' %('none') + else: + lines[10] += '%11s |' %(onoff[PL2[(cab*7)+6]]) + + i = 28 + lines.append('power 48V state = %s' %(onoff[(PL2[i] & 1)])) + lines.append('power LCU state = %s' %(onoff[(PL2[i] >> 1)])) + lines.append('lightning state = %s' %(badok[(PL2[i+1] & 1)])) + + # print lines to screen or file, see printInfo + info = 'status %s (%s) %s ' %(self.station, self.versionstr, time.asctime()) + self.setInfo('-' * len(info)) + self.addInfo(info) + self.addInfo('-' * len(info)) + + for line in lines: + self.addInfo(line) + + def statusIS(self): + onoff = ('OFF','ON') + badok = ('N.A.','OK') + + # get information from EC + self.sendCmd(self.EC_CTRL_TEMP) + (cmdId, status, PL1) = self.recvAck() + self.sendCmd(self.EC_STATUS) + (cmdId, status, PL2) = self.recvAck() + if len(PL1) == 0 or len(PL2) == 0: return + # fill lines with data + lines = [] + lines.append('temperature = %5.2f' %(PL2[2]/100.)) + lines.append('humidity = %5.2f' %(PL2[3]/100.)) + lines.append('heater state = %s' %(onoff[PL2[(3*7)+6]])) + lines.append('power 48V state = %s' %(onoff[(PL2[28] & 1)])) + lines.append('power LCU state = %s' %(onoff[(PL2[28] >> 1) & 1])) + lines.append('lightning state = %s' %(badok[(PL2[29] & 1)])) + + # print lines to screen or file, see printInfo + info1 = ' %s (EC %s)' %(self.station, self.versionstr) + info2 = ' %s ' %(time.asctime()) + self.setInfo('-' * len(info2)) + self.addInfo(info1) + self.addInfo(info2) + self.addInfo('-' * len(info2)) + for line in lines: + self.addInfo(line) + + #--------------------------------------- + def getPowerStatus(self): + state = ('OFF','ON') + # get information from EC + self.sendCmd(self.EC_STATUS) + (cmdId, status, PL) = self.recvAck() + + self.addInfo('Power: 48V = %s, LCU = %s' %(state[(PL[28] & 1)], state[(PL[28] >> 1)])) + + #--------------------------------------- + def getTripStatus(self): + # get information from EC + self.sendCmd(self.EC_STATUS) + (cmdId, status, PL) = self.recvAck() + state = False + if (PL[1] & 0x1000): + self.addInfo('trip in cabinet 0') + state = True + if (PL[8] & 0x1000): + self.addInfo('trip in cabinet 1') + state = True + if (PL[22] & 0x1000): + self.addInfo('trip in cabinet 3') + state = True + + if (PL[1] & 0x6000): + self.addInfo('warning in cabinet 0') + state = True + if (PL[8] & 0x6000): + self.addInfo('warning in cabinet 1') + state = True + if (PL[22] & 0x6000): + self.addInfo('warning in cabinet 3') + state = True + + if (state == False): + self.addInfo('NO trips available') + return(state) + + #--------------------------------------- + ## set observing active(1) or not active(0) + def setObserving(self, state=0): + self.sendCmd(self.SET_OBSERVING, -1, state) + (cmdId, status, PL) = self.recvAck() + self.setInfo('SetObserving to %d' %(state)) + return(self.info) diff --git a/MAC/Tools/Power/status.py b/MAC/Tools/Power/status.py new file mode 100755 index 0000000000000000000000000000000000000000..788de2f4dfeab39be0b8ade3a344e18ef11a5ddf --- /dev/null +++ b/MAC/Tools/Power/status.py @@ -0,0 +1,41 @@ +#!/usr/bin/python + +## Print EC status +## can only be used on LCU +## +## usage: ./status.py +## +## Author: Pieter Donker (ASTRON) +## Last change: September 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.2.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + # version is used to check if function is available in firmware + version,versionstr = ec.getVersion() + ec.printInfo(True) + ec.getStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/status_data.py b/MAC/Tools/Power/status_data.py new file mode 100755 index 0000000000000000000000000000000000000000..9a398f8cea620a9cceb0145561f48c98a2c74ddb --- /dev/null +++ b/MAC/Tools/Power/status_data.py @@ -0,0 +1,61 @@ +#!/usr/bin/python + +""" +write status-data to stdout +stdout format: + time [0] data_cab0 [1] data_cab1 [3] data_cab3 + +values in data_cabx: + temperature humidity fansstate heaterstate + temperature : actual temperature in cabinet + humidity : actual humidity in cabinet + fanstate : which fans are on + bit 0 outer fan front + bit 1 inner fan front + bit 2 inner fan back + bit 4 outer fan back + heaterstate : only available in cabinet 3 + 0 = off + 1 = on + +example, returned data: +1333702601 [0] 24.71 16.81 4 0 [1] 24.72 43.36 4 0 [3] 14.69 41.73 2 0 + +""" + +#from eccontrol import * +#from stations import * +import st_ec_lib as eclib +import sys +import time + +VERSION = '1.1.0' # version of this script +Station = '' + +##======================================================================= +## start of main program +##======================================================================= +def main(): + ec = eclib.EC(eclib.getIP()) + ec.connectToHost() + ec.printInfo(False) # print NOT to screen + + # version is used to check if function is available in firmware + PL2 = ec.getStatusData() + print '%1.0f' %(time.time()), + cabs = [3] + for cab in cabs: + # print cabnr, temperature, humidity, fansstate, heaterstate + print '[%d] %1.2f %1.2f %d %d' %\ + ( cab, PL2[(cab*7)+2]/100., PL2[(cab*7)+3]/100., + PL2[(cab*7)+4] & 0x0f, PL2[(cab*7)+6]), + print + + ##---------------------------------------------------------------------- + ## do not delete next lines + ec.disconnectHost() + + +if __name__ == '__main__': + main() + diff --git a/MAC/Tools/Power/turn_off_48v.py b/MAC/Tools/Power/turn_off_48v.py new file mode 100755 index 0000000000000000000000000000000000000000..e1568c691e92709146c98c19ed1502cfefd213d2 --- /dev/null +++ b/MAC/Tools/Power/turn_off_48v.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +## Turn off 48V powersupply +## can only be used on LCU +## +## usage: ./turn_off_48v.py +## +## Author: Pieter Donker (ASTRON) +## Last change: September 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.0.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + ec.setPower(ec.P_48, ec.PWR_OFF) + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/isTurnOnLCU.py b/MAC/Tools/Power/turn_off_lcu.py similarity index 67% rename from MAC/Tools/Power/isTurnOnLCU.py rename to MAC/Tools/Power/turn_off_lcu.py index 6df46ce50fb48e8408de9dfba66771057b6d0995..b98741767285042fb9190d15d9e310b87ebafe92 100755 --- a/MAC/Tools/Power/isTurnOnLCU.py +++ b/MAC/Tools/Power/turn_off_lcu.py @@ -1,19 +1,18 @@ #!/usr/bin/python -## "isOff48V.py" ## Turn off 48V powersupply on IS (international station) ## can only be used on IS (international) LCU ## -## usage: ./isOff48V.py +## usage: ./turn_off_lcu.py ## ## Author: Pieter Donker (ASTRON) -## Last change: May 2013 +## Last change: September 2014 -from isEcLib import * +from st_ec_lib import * import sys import time -VERSION = '0.0.1' # version of this script +VERSION = '1.0.0' # version of this script # used variables version = 0 # EC version @@ -25,16 +24,16 @@ versionstr = 'V-.-.-' if __name__ == '__main__': host = getIP() if host == None: - print "===============================================" - print "ERROR, this script can only run on a IS station" - print "===============================================" + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" else: ec = EC(host) ec.connectToHost() time.sleep(1.0) ec.printInfo(True) ec.getPowerStatus() - ec.setPower(ec.P_LCU, ec.PWR_ON) + ec.setPower(ec.P_LCU, ec.PWR_OFF) print "waiting 10 seconds" time.sleep(10.0) ec.getPowerStatus() diff --git a/MAC/Tools/Power/turn_on_48v.py b/MAC/Tools/Power/turn_on_48v.py new file mode 100755 index 0000000000000000000000000000000000000000..3fe593af817add875b2db8f949d5b5cbf9842d53 --- /dev/null +++ b/MAC/Tools/Power/turn_on_48v.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +## Turn on 48V powersupply +## can only be used on LCU +## +## usage: ./turn_on_48v.py +## +## Author: Pieter Donker (ASTRON) +## Last change: September 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.0.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + ec.setPower(ec.P_48, ec.PWR_ON) + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/MAC/Tools/Power/turn_on_lcu.py b/MAC/Tools/Power/turn_on_lcu.py new file mode 100755 index 0000000000000000000000000000000000000000..6128edaf5df277e96ed16890c418f2a710049218 --- /dev/null +++ b/MAC/Tools/Power/turn_on_lcu.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +## Turn on LCU power +## can only be used on LCU +## +## usage: ./turn_on_lcu.py +## +## Author: Pieter Donker (ASTRON) +## Last change: September 2014 + +from st_ec_lib import * +import sys +import time + +VERSION = '1.0.0' # version of this script + +# used variables +version = 0 # EC version +versionstr = 'V-.-.-' + +##======================================================================= +## start of main program +##======================================================================= +if __name__ == '__main__': + host = getIP() + if host == None: + print "============================================" + print "ERROR, this script can only run on a station" + print "============================================" + else: + ec = EC(host) + ec.connectToHost() + time.sleep(1.0) + ec.printInfo(True) + ec.getPowerStatus() + ec.setPower(ec.P_LCU, ec.PWR_ON) + print "waiting 10 seconds" + time.sleep(10.0) + ec.getPowerStatus() + ec.printInfo(False) + ec.disconnectHost() + + diff --git a/README b/README index 59c82bd7bf3f428f887f9bc3d2ffa06170e6c656..04c812780068720a696f5c5589a8d4c5b02ce887 100644 --- a/README +++ b/README @@ -13,7 +13,7 @@ Repository web interface: https://svn.astron.nl/viewvc/LOFAR/ Top-level LOFAR Project Content (incomplete summary) -------------------------------- +-------------------------------- CEP/ CEntral Processing software CEP/Calibration/ Calibration: BBS, antenna and station responses @@ -27,6 +27,7 @@ CMake/ CMake configuration & build helper scripts CMake/variants/ compiler- and hostname-specific build configurations CMakeLists.txt Top-level CMake configuration & build script COPYING License text +Docker/ Docker container templates and build scripts INSTALL Build and installation instructions LCS/ LOFAR Common Software: frequently used LOFAR libraries LCS/LofarStMan/ LOFAR Storage Manager for casacore MeasurementSets diff --git a/RTCP/Cobalt/CoInterface/src/Allocator.cc b/RTCP/Cobalt/CoInterface/src/Allocator.cc index ba185a4b90a91d22c2f1cc329127cfb7acb4bb5f..a6b80b9a1d7f8b28f812a2103fa353039df5c72e 100644 --- a/RTCP/Cobalt/CoInterface/src/Allocator.cc +++ b/RTCP/Cobalt/CoInterface/src/Allocator.cc @@ -124,6 +124,11 @@ namespace LOFAR for (SparseSet<void *>::const_iterator it = freeList.getRanges().begin(); it != freeList.getRanges().end(); it++) { void *begin = align(it->begin, alignment); + if ((char *) begin >= (char *) it->end ) { + // alignment shift already results in out of bounds + continue; + } + if ((char *) it->end - (char *) begin >= (ptrdiff_t) size) { // enough space -- reserve it freeList.exclude(begin, (void *) ((char *) begin + size)); diff --git a/RTCP/Cobalt/CoInterface/src/BudgetTimer.cc b/RTCP/Cobalt/CoInterface/src/BudgetTimer.cc index 497604004d38d3c16cca0851d95847cd6ca18c58..324bcfdb84b789ebaa09bbd3baa27a852af8d572 100644 --- a/RTCP/Cobalt/CoInterface/src/BudgetTimer.cc +++ b/RTCP/Cobalt/CoInterface/src/BudgetTimer.cc @@ -45,7 +45,11 @@ namespace LOFAR { const double realTimePerc = 100.0 * getAverage() / budget; if (log_on_destruction) { - LOG_INFO_STR(std::left << std::setw(25) << itsName << ": ran at " << realTimePerc << "% of run-time budget"); + if (realTimePerc >= 100.0) { + LOG_ERROR_STR(std::left << std::setw(25) << itsName << ": ran at " << realTimePerc << "% of run-time budget"); + } else { + LOG_INFO_STR(std::left << std::setw(25) << itsName << ": ran at " << realTimePerc << "% of run-time budget"); + } } else { // TODO } diff --git a/RTCP/Cobalt/CoInterface/src/CorrelatedData.cc b/RTCP/Cobalt/CoInterface/src/CorrelatedData.cc index d55d4474e1b93c5f87ea3ae4489d0a49a2e6c58f..c207e63c26040dd8a44573226f287f78e734cd77 100644 --- a/RTCP/Cobalt/CoInterface/src/CorrelatedData.cc +++ b/RTCP/Cobalt/CoInterface/src/CorrelatedData.cc @@ -76,8 +76,7 @@ namespace LOFAR itsAlignment, allocator, true), - itsNrBytesPerNrValidSamples( - maxNrValidSamples < 256 ? 1 : maxNrValidSamples < 65536 ? 2 : 4) + itsNrBytesPerNrValidSamples(nrBytesPerNrValidSamples(maxNrValidSamples)) { init(nrChannels, allocator); } @@ -100,14 +99,31 @@ namespace LOFAR [NR_POLARIZATIONS], visibilities, false), - itsNrBytesPerNrValidSamples( - maxNrValidSamples < 256 ? 1 : maxNrValidSamples < 65536 ? 2 : 4) + itsNrBytesPerNrValidSamples(nrBytesPerNrValidSamples(maxNrValidSamples)) { ASSERT(this->visibilities.num_elements() == nrVisibilities); init(nrChannels, allocator); } + size_t CorrelatedData::size(unsigned nrStations, + unsigned nrChannels, + unsigned maxNrValidSamples, + unsigned alignment) + { + const size_t nrBaselines = nrStations * (nrStations + 1) / 2; + + return nrBaselines * nrChannels * NR_POLARIZATIONS * NR_POLARIZATIONS * sizeof (fcomplex) + alignment + + nrBaselines * nrChannels * nrBytesPerNrValidSamples(maxNrValidSamples) + 2 * alignment; // nrValidSamples are aligned and padded + } + + + size_t CorrelatedData::nrBytesPerNrValidSamples(size_t maxNrValidSamples) { + return maxNrValidSamples < 256 ? 1 : maxNrValidSamples < 65536 ? 2 : 4; + } + + + void CorrelatedData::init(unsigned nrChannels, Allocator &allocator) { switch (itsNrBytesPerNrValidSamples) { diff --git a/RTCP/Cobalt/CoInterface/src/CorrelatedData.h b/RTCP/Cobalt/CoInterface/src/CorrelatedData.h index edec287e41bf7b654a9cc2634a0ffaec8cb5b8ef..0efe2c42eee2159552a7148307d407e5fc6e221e 100644 --- a/RTCP/Cobalt/CoInterface/src/CorrelatedData.h +++ b/RTCP/Cobalt/CoInterface/src/CorrelatedData.h @@ -49,6 +49,13 @@ namespace LOFAR size_t nrVisibilities, Allocator & = heapAllocator, unsigned alignment = 1); + // Return the maximal memory required for these settings, given a certain alignment. + // Use alignment=0 to get the unaligned memory size required. + static size_t size(unsigned nrStations, unsigned nrChannels, + unsigned maxNrValidSamples, + unsigned alignment = 0); + + CorrelatedData &operator += (const CorrelatedData &); // Fast access to weights; T = uint32_t, uint16_t, or uint8_t, @@ -86,6 +93,8 @@ namespace LOFAR Matrix<uint32_t> itsNrValidSamples4; // [nrBaselines][nrChannels] Matrix<uint16_t> itsNrValidSamples2; // [nrBaselines][nrChannels] Matrix<uint8_t> itsNrValidSamples1; // [nrBaselines][nrChannels] + + static size_t nrBytesPerNrValidSamples(size_t maxNrValidSamples); }; diff --git a/RTCP/Cobalt/CoInterface/src/cmpfloat.cc b/RTCP/Cobalt/CoInterface/src/cmpfloat.cc index e505a17de817f2506f0aa92e9d95cf238e2dc5ef..df0c43d01f571ee799942e68713ac7186d5f45c8 100644 --- a/RTCP/Cobalt/CoInterface/src/cmpfloat.cc +++ b/RTCP/Cobalt/CoInterface/src/cmpfloat.cc @@ -188,8 +188,10 @@ bool compareValues(complex<T> v1, complex<T> v2, double epsilon, size_t pos, T imagFactor = v2.imag() / v1.imag(); // idem if (maxFactors == T(1.0)) { // first unequal val, so 1.0 must be as initialized (not a factor) - maxFactors.real() = minFactors.real() = realFactor; - maxFactors.imag() = minFactors.imag() = imagFactor; + maxFactors.real(realFactor); + minFactors.real(realFactor); + maxFactors.imag(imagFactor); + minFactors.imag(imagFactor); } else { if (realFactor > maxFactors.real()) { maxFactors.real(realFactor); diff --git a/RTCP/Cobalt/CoInterface/test/tcmpfloat.py b/RTCP/Cobalt/CoInterface/test/tcmpfloat.py index 778e557d251b8a44c06445b8b99ed20502969c0f..f27c4f4a1021fbb71b35ec150b385104c78b483d 100755 --- a/RTCP/Cobalt/CoInterface/test/tcmpfloat.py +++ b/RTCP/Cobalt/CoInterface/test/tcmpfloat.py @@ -19,7 +19,12 @@ # # $Id$ -import numpy as np +try: + import numpy as np +except ImportError: + import sys + sys.exit(42) # signal tcmpfloat.sh to skip this test + def main(): # Generate all binary input files diff --git a/RTCP/Cobalt/CoInterface/test/tcmpfloat.sh b/RTCP/Cobalt/CoInterface/test/tcmpfloat.sh index 271e92c7bf12d0bdbcd238c15d41d08d2b6762ed..dfe09a4699f7084f29859241f8506a5756f5cd3b 100755 --- a/RTCP/Cobalt/CoInterface/test/tcmpfloat.sh +++ b/RTCP/Cobalt/CoInterface/test/tcmpfloat.sh @@ -5,7 +5,10 @@ # generate binary input files through tcmpfloat.py ./runctest.sh tcmpfloat -if [ $? -ne 0 ]; then echo "Failed to generate test input files"; exit 1; fi +RESULT=$? + +if [ $RESULT -eq 42 ]; then echo "UNTESTED"; exit 0; fi # skip test +if [ $RESULT -ne 0 ]; then echo "Failed to generate test input files"; exit 1; fi status=0 diff --git a/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.dia b/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.dia deleted file mode 100644 index 2ade22c3509af734be5cd23374492b8fdcefc15e..0000000000000000000000000000000000000000 Binary files a/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.dia and /dev/null differ diff --git a/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.png b/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.png deleted file mode 100644 index 011012b79c382dfe704ba06fcc1b6924179f6cc5..0000000000000000000000000000000000000000 Binary files a/RTCP/Cobalt/GPUProc/doc/Cobalt-hardware.png and /dev/null differ diff --git a/RTCP/Cobalt/GPUProc/doc/DAS4-fs5-jenkins-install-instructions.txt b/RTCP/Cobalt/GPUProc/doc/DAS4-fs5-jenkins-install-instructions.txt deleted file mode 100644 index 89a71eaeed3dcfe2b54cacc4295c5ddab2c085b6..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/GPUProc/doc/DAS4-fs5-jenkins-install-instructions.txt +++ /dev/null @@ -1,213 +0,0 @@ -Used installation commands -========================== - -Keep the latest version of this file in the LOFAR repos under LOFAR/RTCP/Cobalt/GPUProc/doc - - -Environment ------------ - -* We assume the following 'loaded modules' (e.g. through .bashrc): - module load gcc # default on DAS-4 - module load sge # idem - module load fftw3/gcc/64 - module load openmpi/gcc/64 - module load cuda55/toolkit - module load cuda55/fft - module load hwloc - (If the jenkins system is in control (JENKINS_SERVER_COOKIE has a value), - we set modules and PATH-like env vars in the jenkins build/test scripts.) -* We assume PATH contains $HOME/root/bin - and LD_LIBRARY_PATH contains $HOME/root/lib - and PYTHONPATH contains $HOME/root/lib64/python2.6/site-packages - -* Always pass at least -DCMAKE_INSTALL_PREFIX=$HOME/root to cmake, or --prefix=$HOME/root to ./configure. -* Make sure everything is linked using the same library versions (esp. for libhdf5). - - -Relevant files and directories ------------------------------- - -$HOME/root/ installation root -$HOME/root/src/ source and build directories -$HOME/root/share/aips++/data/ CASA measures table location -$HOME/jenkins/ location of the jenkins continuous build and integration system - - -log4cplus (easier to install using your OS' package manager) ---------- - -Dependency for: LOFAR - - cd $HOME/root/src - wget http://downloads.sourceforge.net/project/log4cplus/log4cplus-stable/1.1.1/log4cplus-1.1.1.tar.gz # or whatever recent version you like - tar zxf log4cplus-1.1.1.tar.gz - cd log4cplus-1.1.1 - ./configure --prefix=$HOME/root - make -j 8 install - make check # optional - - -libssh2 (easier to install using your OS' package manager) -------- - -Dependency for: LOFAR - - cd $HOME/root/src - wget http://www.libssh2.org/download/libssh2-1.4.3.tar.gz # or whatever recent version you like - tar zxf libssh2-1.4.3.tar.gz - cd libssh2-1.4.3 - ./configure --prefix=$HOME/root - make -j 8 install - make check # optional - - -cfitsio (easier to install using your OS' package manager) -------- - -Dependency for: casacore, wcslib - - cd $HOME/root/src - wget ftp://heasarc.gsfc.nasa.gov/software/fitsio/c/cfitsio_latest.tar.gz - tar zxf cfitsio_latest.tar.gz - cd cfitsio - ./configure --prefix=$HOME/root --enable-reentrant --enable-sse2 --enable-ssse3 - make -j 8 shared - make install - - -wcslib (easier to install using your OS' package manager) ------- - -Dependency for: casacore - - cd $HOME/root/src - wget ftp://ftp.atnf.csiro.au/pub/software/wcslib/wcslib.tar.bz2 - tar jxf wcslib.tar.bz2 - cd wcslib-4.18 # or whatever version you have - ./configure --prefix=$HOME/root --with-cfitsiolib=$HOME/root/lib --with-cfitsioinc=$HOME/root/include - make -j 8 install - - -CASA measures tables --------------------- - -Dependency for: casacore (keep the measure tables up to date, automatically!) - - cd $HOME/root/share # or wherever - mkdir aips++ - cd aips++ - wget ftp://ftp.atnf.csiro.au/pub/software/measures_data/measures_data.tar.bz2 ftp://ftp.atnf.csiro.au/pub/software/measures_data/measures_data.tar.bz2.md5sum - md5sum measures_data.tar.bz2 # verify that md5 hash is equal to hash in measures_data.tar.bz2.md5sum (if you automate it, note that the .md5sum file contains a CSIRO full path name) - tar jxf measures_data.tar.bz2 - - # Now, add updating it to a cron or jenkins job - - -casacore --------- - -Dependency for: LOFAR/RTCP/Cobalt - -Since we keep the measure data up to date (and since this install is the testing env standard), -we use -DDATA_DIR=..., such that others can use our install AND measures tables. - - cd $HOME/root/src - svn co http://casacore.googlecode.com/svn/trunk/ casacore - mkdir casacore/build - cd casacore/build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/root -D-DUSE_HDF5=ON -DUSE_FFTW3=ON -DUSE_THREADS=ON -DUSE_OPENMP=ON -DCXX11=ON -DBUILD_PYTHON=ON -DUSE_STACKTRACE=ON -DDATA_DIR=$HOME/casacore/data .. - make -j 8 install - ctest # optional - - findmeastable # optional; verify that casacore can find the measures tables - - -casarest (not needed for LOFAR Cobalt) --------- - -Dependency for: LOFAR Calibration package, maybe others - - cd $HOME/root/src - svn co https://svn.astron.nl/casarest/trunk/casarest - mkdir casarest/build - cd casarest/build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/root -DCASACORE_ROOT_DIR=$HOME/root -DLIB_EXTRA_SYNTHESIS=gfortran -DBUILD_ALL=1 .. # if you need ALL... - # On DAS-4 casarest r8758, 'make' searches for a boost lib in /usr/lib64/lib64. Patch that away. - # grep -r -l lib64/lib64 * | xargs sed -i -e 's/lib64\/lib64/lib64\//g' - make -j 8 install - - -python-casacore (formerly pyrap) (optional for LOFAR Cobalt) ------ - -Dependency for: various LOFAR/CEP packages -Depends on: python boost-python casacore numpy scons - - cd $HOME/root/src - git clone https://github.com/casacore/python-casacore.git - cd python-casacore - ./setup.py build_ext -I$HOME/root/include -L$HOME/root/lib # casacore include and lib paths - ./setup install --prefix=$HOME/root - python -c 'import casacore' # optional (test), should print nothing and exit 0 - - -blitz++ (easier to install using your OS' package manager, not needed for LOFAR Cobalt) -------- - -Dependency for: some LOFAR MAC packages - - cd $HOME/root/src - wget http://sourceforge.net/projects/blitz/files/blitz/Blitz%2B%2B%200.10/blitz-0.10.tar.gz # or whatever recent version you like - tar zxf blitz-0.10.tar.gz - cd blitz-0.10 - ./configure --prefix=$HOME/root --enable-shared --enable-optimize - make -j 8 install - - -valgrind (easier to install using your OS' package manager; optional for LOFAR Cobalt) --------- -needed for mpi to prevent tons of false positives - -Dependency for: running LOFAR tests under valgrind - - cd $HOME/root/src - wget http://valgrind.org/downloads/valgrind-3.10.1.tar.bz2 # or whatever version you like - tar jxf valgrind-3.10.1.tar.bz2 - cd valgrind-3.10.1 - ./configure --prefix=$HOME/root --with-mpicc=/cm/shared/apps/openmpi/gcc/64/1.4.4/bin/mpicc -# on cobalt, use: ./configure --prefix=/localhome/lofar/valgrind-3.10.1 --with-mpicc=/opt/openmpi/bin/mpicc - make -j 8 install - valgrind ls -l # optional (test), should exit 0 - - -Data Access Library (DAL) -------------------------- - -Dependency for: LOFAR/RTCP/Cobalt/OutputProc - - cd $HOME/root/src - git clone https://github.com/nextgen-astrodata/DAL.git DAL - mkdir DAL/build - cd DAL/build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/root .. - make -j 8 install - ctest # optional - python -c 'import dal' # optional (test), should print nothing and exit 0 - - -LOFAR (Cobalt only) -------------------- - - cd $HOME/root/src - svn co --username USERNAME -N https://svn.astron.nl/LOFAR/trunk LOFAR - cd LOFAR - svn up CMake # cmake will fetch what is needed - cd .. - mkdir -p LOFAR-build/gnu_opt - cd LOFAR-build/gnu_opt - cmake -DCMAKE_INSTALL_PREFIX=$HOME/root -DBUILD_PACKAGES="Cobalt StaticMetaData" -DUSE_CUDA=ON -DUSE_OPENMP=ON -DUSE_MPI=ON ../../LOFAR # assumes you have all deps installed and a LOFAR/CMake/variants/variants.<hostname> file is in place - make -j 8 install - ctest -j 8 # optional - . $HOME/root/lofarinit.sh # required to use some of the LOFAR tools, but we will do this through jenkins - diff --git a/RTCP/Cobalt/GPUProc/doc/cobalt-data-flow/cobalt-data-flow.eap b/RTCP/Cobalt/GPUProc/doc/cobalt-data-flow/cobalt-data-flow.eap index 167741ee5ed1163610fc0029a95869683ed2726e..2a67d0f69a127cc34f6f84e18e85aa4bb00b702f 100644 Binary files a/RTCP/Cobalt/GPUProc/doc/cobalt-data-flow/cobalt-data-flow.eap and b/RTCP/Cobalt/GPUProc/doc/cobalt-data-flow/cobalt-data-flow.eap differ diff --git a/RTCP/Cobalt/GPUProc/doc/pipeline-buffers.txt b/RTCP/Cobalt/GPUProc/doc/pipeline-buffers.txt index be7f8a9a82b2126d0c157403a1e0ccc7438cf504..28b060618622c994f8c29b97d196e79df70a18ca 100644 --- a/RTCP/Cobalt/GPUProc/doc/pipeline-buffers.txt +++ b/RTCP/Cobalt/GPUProc/doc/pipeline-buffers.txt @@ -68,7 +68,7 @@ FFT-shift {inplace} FFT-64 {inplace} | [station][pol][sample][channel] [48][2][3072][64] = 144 MiB B V -Delay compensation + Transpose {I/O: delays} +Delay compensation + Transpose (implicit, DO_TRANSPOSE not defined) {I/O: delays} | [station][pol][channel][sample] [48][2][64][3072] = 144 MiB A V FFT-shift {inplace} diff --git a/RTCP/Cobalt/GPUProc/etc/parset-additions.d/default/NoCascadingFFT.parset b/RTCP/Cobalt/GPUProc/etc/parset-additions.d/default/NoCascadingFFT.parset index ac1c9480b8a9c3db76cc54b3c6f657d5efcda908..bc5839123d71bf72a2a21607c68057390aa6b74e 100644 --- a/RTCP/Cobalt/GPUProc/etc/parset-additions.d/default/NoCascadingFFT.parset +++ b/RTCP/Cobalt/GPUProc/etc/parset-additions.d/default/NoCascadingFFT.parset @@ -1,5 +1,5 @@ # $Id$ # Define the sizes of both FFTs in the BF pipeline, # to prevent a cascade of FFTs -Cobalt.BeamFormer.nrDelayCompensationChannels = 64 -Cobalt.BeamFormer.nrHighResolutionChannels = 64 +Cobalt.BeamFormer.nrDelayCompensationChannels = 256 +Cobalt.BeamFormer.nrHighResolutionChannels = 256 diff --git a/RTCP/Cobalt/GPUProc/share/gpu/kernels/BeamFormer.cu b/RTCP/Cobalt/GPUProc/share/gpu/kernels/BeamFormer.cu index cb9a3a12e7eb6d9a96146ed002108ece39326de2..a293c15b1131fe4bae47d5a7bf2832723c1a1573 100644 --- a/RTCP/Cobalt/GPUProc/share/gpu/kernels/BeamFormer.cu +++ b/RTCP/Cobalt/GPUProc/share/gpu/kernels/BeamFormer.cu @@ -29,7 +29,7 @@ #define NR_STATIONS_PER_PASS ((NR_STATIONS + NR_PASSES - 1) / NR_PASSES) #endif #if NR_STATIONS_PER_PASS > 32 -#error "need more passes to beam for this number of stations" +#error "need more passes to beamform this number of stations" #endif #ifdef FLYS_EYE diff --git a/RTCP/Cobalt/GPUProc/src/Storage/StorageProcess.cc b/RTCP/Cobalt/GPUProc/src/Storage/StorageProcess.cc index 8927cce9c00d131806d606d2a2eca648dba744a2..a56faadc905fdfbb5774adfdc2923c95433a46de 100644 --- a/RTCP/Cobalt/GPUProc/src/Storage/StorageProcess.cc +++ b/RTCP/Cobalt/GPUProc/src/Storage/StorageProcess.cc @@ -73,7 +73,7 @@ namespace LOFAR if (itsParset.settings.correlator.enabled) for (size_t i = 0; i < itsParset.settings.correlator.files.size(); ++i) { - LOG_INFO_STR(itsParset.settings.correlator.files[i].location.host << " == " << itsHostname); + LOG_DEBUG_STR(itsParset.settings.correlator.files[i].location.host << " == " << itsHostname); if (itsParset.settings.correlator.files[i].location.host == itsHostname) { Protocols::TaskFeedbackDataproducts msg( diff --git a/RTCP/Cobalt/GPUProc/src/cuda/Pipelines/Pipeline.cc b/RTCP/Cobalt/GPUProc/src/cuda/Pipelines/Pipeline.cc index e84a3048457c4254749abbb2ee17ed272e8b8956..a168d03ebb2d77dfda1973d4a351efcbeb732727 100644 --- a/RTCP/Cobalt/GPUProc/src/cuda/Pipelines/Pipeline.cc +++ b/RTCP/Cobalt/GPUProc/src/cuda/Pipelines/Pipeline.cc @@ -213,7 +213,7 @@ namespace LOFAR //sections = program segments defined by the following omp section directive // are distributed for parallel execution among available threads //parallel = directive explicitly instructs the compiler to parallelize the chosen block of code. - // The two sections in this function are done in parallel with a seperate set of threads. + // The 6 sections in this function are done in parallel with seperate threads. # pragma omp parallel sections num_threads(6) { diff --git a/RTCP/Cobalt/GPUProc/src/cuda/SubbandProcs/SubbandProc.cc b/RTCP/Cobalt/GPUProc/src/cuda/SubbandProcs/SubbandProc.cc index 404d92618c6268f4afa13d430625b31b5ffb2213..79810a2bcd9272db50967ee5cb82aa344fd76158 100644 --- a/RTCP/Cobalt/GPUProc/src/cuda/SubbandProcs/SubbandProc.cc +++ b/RTCP/Cobalt/GPUProc/src/cuda/SubbandProcs/SubbandProc.cc @@ -86,7 +86,7 @@ namespace LOFAR } // NOTE: For an explanation of the different buffers being used, please refer - // to the document bf-pipeline.txt in the GPUProc/doc directory. + // to the document pipeline-buffers.txt in the GPUProc/doc directory. devA.reset(new gpu::DeviceMemory(context, devA_size)); devB.reset(new gpu::DeviceMemory(context, devB_size)); diff --git a/RTCP/Cobalt/GPUProc/src/scripts/Cobalt_install.sh b/RTCP/Cobalt/GPUProc/src/scripts/Cobalt_install.sh index fa1531c76d5c22877d949e502ee72ffe64dac1bf..109c94d97b2bed42cdab9ccd9f6bacec18682a2c 100755 --- a/RTCP/Cobalt/GPUProc/src/scripts/Cobalt_install.sh +++ b/RTCP/Cobalt/GPUProc/src/scripts/Cobalt_install.sh @@ -31,7 +31,7 @@ for HOST in ${HOSTS:-cbm001 cbm002 cbm003 cbm004 cbm005 cbm006 cbm007 cbm008 cbm mkdir -p \$dl_dir && cd \$dl_dir || exit 1 # Download archive from NEXUS. -N: clobber existing files - wget -N --tries=3 --no-check-certificate \"${NEXUS_URL}\" || exit 1 + wget -N --tries=3 --no-check-certificate --user=macinstall --password=macinstall \"${NEXUS_URL}\" || exit 1 # The full pathnames are in the tar file, so unpack from root dir. # -m: don't warn on timestamping /localhome @@ -52,7 +52,7 @@ for HOST in ${HOSTS:-cbm001 cbm002 cbm003 cbm004 cbm005 cbm006 cbm007 cbm008 cbm OUTPUTPROC_CAPABILITIES='cap_sys_nice,cap_ipc_lock' sudo /sbin/setcap \"${OUTPUTPROC_CAPABILITIES}\"=ep bin/outputProc || true RTCP_CAPABILITIES='cap_net_raw,cap_sys_nice,cap_ipc_lock' - sudo /sbin/setcap \"${RTCP_CAPABILITIES}\"=ep bin/rtcp || true + sudo /sbin/setcap \"${RTCP_CAPABILITIES}\"=ep bin/rtcp " || exit 1 done diff --git a/RTCP/Cobalt/GPUProc/src/scripts/cobalt_functions.sh b/RTCP/Cobalt/GPUProc/src/scripts/cobalt_functions.sh index 301a14087ab925df3f35217a5c3bd52d17911112..e59a910c4dd99dfe5a4969073206433e0184a072 100755 --- a/RTCP/Cobalt/GPUProc/src/scripts/cobalt_functions.sh +++ b/RTCP/Cobalt/GPUProc/src/scripts/cobalt_functions.sh @@ -52,25 +52,32 @@ function read_cluster_model { case "${CLUSTER_NAME}" in CEP4) HEADNODE=head01.cep4.control.lofar - #COMPUTENODES="`seq -f "cpu%02.0f.cep4" 1 50`" - COMPUTENODES="`seq -f "cpu%02.0f.cep4" 1 26` `seq -f "cpu%02.0f.cep4" 30 35` `seq -f "cpu%02.0f.cep4" 37 39`" - NRCOMPUTENODES=50 + COMPUTENODES="`ssh $HEADNODE sinfo --responding --states=idle,mixed,alloc --format=%n.cep4,%T --noheader --partition=cobalt --sort=N | fgrep -v ,draining | cut -f1 -d,`" + if [ -z "$COMPUTENODES" ]; then + echo "ERROR: Could not obtain list of available CEP4 nodes. Defaulting to all." + COMPUTENODES="`seq -f "cpu%02.0f.cep4" 1 50`" + fi GLOBALFS_DIR=/data #SLURM=true SLURM=false # Don't use SLURM for now, let's get it working without it first GLOBALFS=true - DOCKER=true + DOCKER=false # disabled as outputproc is too slow on docker 1.9.1 (#9522) + + OUTPUTPROC_ROOT="`echo '/opt/outputproc-${LOFAR_TAG}' | docker-template`" ;; *) HEADNODE=lhn001.cep2.lofar COMPUTENODES="`seq -f "locus%02.0f" 1 94`" - NRCOMPUTENODES=94 SLURM=false GLOBALFS=false DOCKER=false + + OUTPUTPROC_ROOT="/data/home/lofarsys/production/lofar_cobalt" ;; esac + + NRCOMPUTENODES=`echo $COMPUTENODES | wc -w` } diff --git a/RTCP/Cobalt/GPUProc/src/scripts/runObservation.sh b/RTCP/Cobalt/GPUProc/src/scripts/runObservation.sh index 8cf04b750e240dd146d4c90ab1b91f811aca9c91..aadd9ffbac02f6b5c3ab48fbfd87868e19c1ad31 100755 --- a/RTCP/Cobalt/GPUProc/src/scripts/runObservation.sh +++ b/RTCP/Cobalt/GPUProc/src/scripts/runObservation.sh @@ -252,8 +252,23 @@ read_cluster_model # Determine list of outputProc hosts for various purposes if $SLURM; then + # TODO: Start SLURM job, wait for resources, record node list + echo "ERROR: SLURM resource allocation is not supported" + exit 1 + + # Allocate resources + # TODO: Start outputProc here + ssh $HEADNODE srun -N $NRCOMPUTENODES -c 1 --job-name=$OBSID bash -c 'while sleep 1; do :; done' & + + # Wait for allocation + while [ "`ssh $HEADNODE sacct --starttime=now --noheader --parsable2 --format=state --name=$OBSID | tail -n 1`" != "RUNNING" ]; do sleep 1; done + + # Obtain node list + NODE_LIST="`ssh $HEADNODE sacct --starttime=now --noheader --parsable2 --format=nodelist --name=$OBSID | tail -n 1`" + # Expand node list into something usable - NODE_LIST="`ssh $HEADNODE scontrol show hostnames $SLURM_JOB_NODELIST`" + # TODO: move ".cep4" to cluster model + NODE_LIST="`ssh $HEADNODE scontrol show hostnames $NODE_LISTi | awk '{ print $1 ".cep4"; }'`" else # Derive host list from parset NODE_LIST=$(getOutputProcHosts $PARSET) @@ -304,6 +319,11 @@ PID_LIST_FILE="$LOFARROOT/var/run/outputProc-$OBSERVATIONID.pids" function clean_up { EXIT_STATE=$1 PID_LIST=$2 + + if $SLURM; then + echo "[children] Cancelling SLURM allocation" + ssh $HEADNODE scancel --jobname=$OBSID + fi echo "[children] Sending SIGTERM" # THe kill statements might be called with an empty argument. This will @@ -335,14 +355,15 @@ echo "[outputProc] pid file = $PID_LIST_FILE" touch $PID_LIST_FILE # Construct full command line for outputProc -OUTPUTPROC_VARS="export QUEUE_PREFIX=$QUEUE_PREFIX LOFARENV=$LOFARENV;" # Variables to forward to outputProc -OUTPUTPROC_CMDLINE="$OUTPUTPROC_VARS $OUTPUT_PROC_EXECUTABLE $OBSERVATIONID" +OUTPUTPROC_CMDLINE="source $OUTPUTPROC_ROOT/lofarinit.sh; export QUEUE_PREFIX=$QUEUE_PREFIX LOFARENV=$LOFARENV; $OUTPUT_PROC_EXECUTABLE $OBSERVATIONID" # Wrap command line with Docker if required if $DOCKER; then TAG="`echo '${LOFAR_TAG}' | docker-template`" - OUTPUTPROC_CMDLINE="docker run --rm --privileged -e LUSER=`id -u $SSH_USER_NAME` --net=host -v $GLOBALFS_DIR:$GLOBALFS_DIR lofar-outputproc:$TAG bash -c \"$OUTPUTPROC_CMDLINE\"" + # "echo 950000 > /sys/fs/cgroup/cpu/cpu.rt_runtime_us" is needed to configure the real-time scheduler for this cgroup + # --cpu-shares=24576 (=24*1024) provides this process with as much share of the CPU as 24 other containers (note: CEP4 has 24 cores/node) + OUTPUTPROC_CMDLINE="docker-run-slurm.sh --rm --cpu-shares=24576 --cap-add=sys_nice --cap-add=sys_admin -u `id -u $SSH_USER_NAME` --net=host -v $GLOBALFS_DIR:$GLOBALFS_DIR lofar-outputproc:$TAG bash -c \"sudo echo 950000 > /sys/fs/cgroup/cpu/cpu.rt_runtime_us; $OUTPUTPROC_CMDLINE\"" fi echo "[outputProc] command line = $OUTPUTPROC_CMDLINE" diff --git a/RTCP/Cobalt/GPUProc/src/scripts/startBGL.sh b/RTCP/Cobalt/GPUProc/src/scripts/startBGL.sh index 99e90f9aae27a5e381c1e05ee4ac91adb0894aaa..5103ddc82f6c4e8b750ea04eb2d9c96ba852e2c7 100755 --- a/RTCP/Cobalt/GPUProc/src/scripts/startBGL.sh +++ b/RTCP/Cobalt/GPUProc/src/scripts/startBGL.sh @@ -63,21 +63,6 @@ mkfifo -m 0660 "$COMMANDPIPE" || true # Construct command line COMMAND="env LOFARENV=$LOFARENV runObservation.sh -P $PIDFILE -o Cobalt.commandStream=file:$COMMANDPIPE $PARSET" -# Process cluster requirements -read_cluster_model - -if $SLURM; then - # We need to issue "salloc" on the target cluster, and once the resources - # are available, the job should first SSH back here to start the observation. - - # Note that we need to marshall some SLURM environment variables as well, hence the use of bash. - for s in SLURM_JOB_ID SLURM_JOB_NODELIST SLURM_JOB_NUM_NODES; do - SLURM_VARS+=" $s=\$$s" - done - - COMMAND="ssh -tt $HEADNODE salloc -N $NRCOMPUTENODES -J $OBSID bash -c 'ssh `hostname -f` -tt $SLURM_VARS $COMMAND'" -fi - # Start observation in the background echo "Starting $COMMAND" $COMMAND > $LOGFILE 2>&1 </dev/null & diff --git a/RTCP/Cobalt/GPUProc/test/cuda/tBandPassCorrection.cc b/RTCP/Cobalt/GPUProc/test/cuda/tBandPassCorrection.cc index ab505a98f921411c92a0bb48cf01e6ec1ce2d614..866a4e9cd1655d97b1701694645cb4577670a267 100644 --- a/RTCP/Cobalt/GPUProc/test/cuda/tBandPassCorrection.cc +++ b/RTCP/Cobalt/GPUProc/test/cuda/tBandPassCorrection.cc @@ -166,8 +166,8 @@ vector<fcomplex> runTest(const CompileDefinitions& compileDefs) // set inputs for (size_t i = 0; i < inputData->num_elements(); i++) { - inputData->origin()[i].real() = 1.0f; - inputData->origin()[i].imag() = 1.0f; + inputData->origin()[i].real(1.0f); + inputData->origin()[i].imag(1.0f); } for (size_t i = 1; i < bandPassFactors.num_elements(); i++) { @@ -176,8 +176,8 @@ vector<fcomplex> runTest(const CompileDefinitions& compileDefs) // set output for proper verification later for (size_t i = 0; i < outputData->num_elements(); i++) { - outputData->origin()[i].real() = 42.0f; - outputData->origin()[i].imag() = 42.0f; + outputData->origin()[i].real(42.0f); + outputData->origin()[i].imag(42.0f); } gpu::Function kfunc(initKernel(ctx, compileDefs)); diff --git a/RTCP/Cobalt/GPUProc/test/cuda/tDelayAndBandPass.cc b/RTCP/Cobalt/GPUProc/test/cuda/tDelayAndBandPass.cc index 53bcee0c064d072e3b3c329eb7c33b8295d7dd9d..d41fd47db075b45b35ab7deaab6c0f8363fad76f 100644 --- a/RTCP/Cobalt/GPUProc/test/cuda/tDelayAndBandPass.cc +++ b/RTCP/Cobalt/GPUProc/test/cuda/tDelayAndBandPass.cc @@ -244,8 +244,8 @@ vector<fcomplex> runTest(const CompileDefinitions& compileDefs, // set inputs for (size_t i = 0; i < inputData->num_elements(); i++) { - inputData->origin()[i].real() = 1.0f; - inputData->origin()[i].imag() = 1.0f; + inputData->origin()[i].real(1.0f); + inputData->origin()[i].imag(1.0f); } for (size_t i = 0; i < delaysAtBegin.num_elements(); i++) { delaysAtBegin.origin()[i] = delayBegin; @@ -262,8 +262,8 @@ vector<fcomplex> runTest(const CompileDefinitions& compileDefs, // set output for proper verification later for (size_t i = 0; i < outputData->num_elements(); i++) { - outputData->origin()[i].real() = 42.0f; - outputData->origin()[i].imag() = 42.0f; + outputData->origin()[i].real(42.0f); + outputData->origin()[i].imag(42.0f); } gpu::Function kfunc(initKernel(ctx, compileDefs)); diff --git a/RTCP/Cobalt/GPUProc/test/cuda/tIntToFloat.cc b/RTCP/Cobalt/GPUProc/test/cuda/tIntToFloat.cc index baef5de12a7b95684caee72844fff6b7d13aa2af..fee1b377c05fbea0f0d1efbc882700c24d6b67b8 100644 --- a/RTCP/Cobalt/GPUProc/test/cuda/tIntToFloat.cc +++ b/RTCP/Cobalt/GPUProc/test/cuda/tIntToFloat.cc @@ -126,8 +126,8 @@ vector<complex<float> > runTest(int defaultVal) // set input for (size_t i = 0; i < input.num_elements(); i++) { - input.origin()[i].real() = defaultVal; - input.origin()[i].imag() = defaultVal; + input.origin()[i].real(defaultVal); + input.origin()[i].imag(defaultVal); } // set output for proper verification later @@ -157,13 +157,13 @@ vector<complex<float> > runTest(int defaultVal) template <typename T> void setSample(T& sample, int& val) { - sample.real() = val; + sample.real(val); if (val == numeric_limits<typename T::value_type>::max()) val = numeric_limits<typename T::value_type>::min(); // wrap back to minimum else val += 1; - sample.imag() = val; + sample.imag(val); if (val == numeric_limits<typename T::value_type>::max()) val = numeric_limits<typename T::value_type>::min(); // wrap back to minimum else diff --git a/RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.py b/RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.py index cef56e81940d34bc8d7cf6b824ab9667fa385fee..f21c5e083e8887357ecd1970363893614742388b 100644 --- a/RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.py +++ b/RTCP/Cobalt/GPUProc/test/t_generate_globalfs_locations.py @@ -45,7 +45,7 @@ class ProcessParset(unittest.TestCase): self.assertTrue(l.startswith("foo:") or l.startswith("bar:")) def main(argv): - unittest.main(verbosity=2) + unittest.main() if __name__ == "__main__": # run all tests diff --git a/RTCP/Cobalt/InputProc/src/CMakeLists.txt b/RTCP/Cobalt/InputProc/src/CMakeLists.txt index bcbe83a534ec8509b3e71ef12f59a920224d7dc7..ff6c208a3ad0e02414dbff6d22f46c4d7f44b093 100644 --- a/RTCP/Cobalt/InputProc/src/CMakeLists.txt +++ b/RTCP/Cobalt/InputProc/src/CMakeLists.txt @@ -49,3 +49,4 @@ configure_file( ${CMAKE_BINARY_DIR}/bin/mpirun.sh @ONLY) lofar_add_bin_scripts(${CMAKE_BINARY_DIR}/bin/mpirun.sh) +lofar_add_bin_scripts(ping_intl.sh) diff --git a/RTCP/Cobalt/InputProc/src/Transpose/MPIUtil.cc b/RTCP/Cobalt/InputProc/src/Transpose/MPIUtil.cc index 30b90c677c1e844f8fef0a8e31bf332aeae0bce7..9276d97089104232d28b244da14c752241597605 100644 --- a/RTCP/Cobalt/InputProc/src/Transpose/MPIUtil.cc +++ b/RTCP/Cobalt/InputProc/src/Transpose/MPIUtil.cc @@ -126,44 +126,56 @@ namespace LOFAR { MPIAllocator mpiAllocator; - namespace { - typedef int (*MPI_SEND)(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request*); - - // Generic send method - MPI_Request Guarded_MPI_Send(MPI_SEND sendMethod, const void *ptr, size_t numBytes, int destRank, int tag) { - DEBUG("SEND: size " << numBytes << " tag " << hex << tag << dec << " to " << destRank); - - ASSERT(numBytes > 0); - ASSERT(tag >= 0); // Silly MPI requirement - - //SmartPtr<ScopedLock> sl = MPI_threadSafe() ? 0 : new ScopedLock(MPIMutex); - - MPI_Request request; - - int error; - - error = sendMethod(const_cast<void*>(ptr), numBytes, MPI_BYTE, destRank, tag, MPI_COMM_WORLD, &request); - ASSERT(error == MPI_SUCCESS); - - return request; - } - } - - MPI_Request Guarded_MPI_Issend(const void *ptr, size_t numBytes, int destRank, int tag) { - return Guarded_MPI_Send(::MPI_Issend, ptr, numBytes, destRank, tag); + DEBUG("SEND: size " << numBytes << " tag " << hex << tag << dec << " to " << destRank); + ASSERT(numBytes > 0); + ASSERT(tag >= 0); // Silly MPI requirement (Reason: MPI_ANY_TAG is -1, but only for receivers) + //SmartPtr<ScopedLock> sl = MPI_threadSafe() ? 0 : new ScopedLock(MPIMutex); + + MPI_Request request; + // const_cast is for missing const in protos in OpenMPI <=1.6 (1.10 is fixed, not sure in between) + int error = ::MPI_Issend(const_cast<void*>(ptr), numBytes, MPI_BYTE, destRank, tag, MPI_COMM_WORLD, &request); + ASSERT(error == MPI_SUCCESS); + return request; } MPI_Request Guarded_MPI_Irsend(const void *ptr, size_t numBytes, int destRank, int tag) { - return Guarded_MPI_Send(::MPI_Irsend, ptr, numBytes, destRank, tag); + DEBUG("SEND: size " << numBytes << " tag " << hex << tag << dec << " to " << destRank); + ASSERT(numBytes > 0); + ASSERT(tag >= 0); // Silly MPI requirement (Reason: MPI_ANY_TAG is -1, but only for receivers) + //SmartPtr<ScopedLock> sl = MPI_threadSafe() ? 0 : new ScopedLock(MPIMutex); + + MPI_Request request; + // const_cast is for missing const in protos in OpenMPI <=1.6 (1.10 is fixed, not sure in between) + int error = ::MPI_Irsend(const_cast<void*>(ptr), numBytes, MPI_BYTE, destRank, tag, MPI_COMM_WORLD, &request); + ASSERT(error == MPI_SUCCESS); + return request; } MPI_Request Guarded_MPI_Ibsend(const void *ptr, size_t numBytes, int destRank, int tag) { - return Guarded_MPI_Send(::MPI_Ibsend, ptr, numBytes, destRank, tag); + DEBUG("SEND: size " << numBytes << " tag " << hex << tag << dec << " to " << destRank); + ASSERT(numBytes > 0); + ASSERT(tag >= 0); // Silly MPI requirement (Reason: MPI_ANY_TAG is -1, but only for receivers) + //SmartPtr<ScopedLock> sl = MPI_threadSafe() ? 0 : new ScopedLock(MPIMutex); + + MPI_Request request; + // const_cast is for missing const in protos in OpenMPI <=1.6 (1.10 is fixed, not sure in between) + int error = ::MPI_Ibsend(const_cast<void*>(ptr), numBytes, MPI_BYTE, destRank, tag, MPI_COMM_WORLD, &request); + ASSERT(error == MPI_SUCCESS); + return request; } MPI_Request Guarded_MPI_Isend(const void *ptr, size_t numBytes, int destRank, int tag) { - return Guarded_MPI_Send(::MPI_Isend, ptr, numBytes, destRank, tag); + DEBUG("SEND: size " << numBytes << " tag " << hex << tag << dec << " to " << destRank); + ASSERT(numBytes > 0); + ASSERT(tag >= 0); // Silly MPI requirement (Reason: MPI_ANY_TAG is -1, but only for receivers) + //SmartPtr<ScopedLock> sl = MPI_threadSafe() ? 0 : new ScopedLock(MPIMutex); + + MPI_Request request; + // const_cast is for missing const in protos in OpenMPI <=1.6 (1.10 is fixed, not sure in between) + int error = ::MPI_Isend(const_cast<void*>(ptr), numBytes, MPI_BYTE, destRank, tag, MPI_COMM_WORLD, &request); + ASSERT(error == MPI_SUCCESS); + return request; } MPI_Request Guarded_MPI_Irecv(void *ptr, size_t numBytes, int srcRank, int tag) { diff --git a/RTCP/Cobalt/InputProc/src/ping_intl.sh b/RTCP/Cobalt/InputProc/src/ping_intl.sh new file mode 100755 index 0000000000000000000000000000000000000000..7634f03c8b459308826e48464c8f14af2090612e --- /dev/null +++ b/RTCP/Cobalt/InputProc/src/ping_intl.sh @@ -0,0 +1,32 @@ +#!/bin/bash -u +echo "Begin `date`" +echo "Host `hostname`" + +HOSTNAME=`hostname` +RSPCONNECTIONS=$LOFARROOT/etc/StaticMetaData/RSPConnections_Cobalt.dat + +# International stations have the name xx6xx. +# +# For each International Station VLAN, Cobalt has IP 10.x.x.50, +# and we need to ping 10.x.x.1. +SRCIPS=` + <$RSPCONNECTIONS fgrep $HOSTNAME | + grep -v "^#" | + perl -ne ' + if (/^..60?(..?) .* 10[.]([0-9]+)[.]([0-9]+)[.]1../) { + if ($3 == 1) { + print "10.$2.$1.1\n" + } else { + print "10.$2.$3.1\n" + } + } ' + +echo "IPs $SRCIPS" + +for IP in $SRCIPS +do + # Ping this RSP board, but don't wait for an answer + ping -p 10fa -q -n -c 1 -w 1 $IP +done + +echo "End `date`" diff --git a/RTCP/Cobalt/OutputProc/etc/sudoers.d/setcap_cobalt b/RTCP/Cobalt/OutputProc/etc/sudoers.d/setcap_cobalt new file mode 100644 index 0000000000000000000000000000000000000000..df16728cb6d223498df74d22d76e50992766e2ab --- /dev/null +++ b/RTCP/Cobalt/OutputProc/etc/sudoers.d/setcap_cobalt @@ -0,0 +1,5 @@ +## Allows lofarbuild to add the listed capabilities to any single writable file for automated roll-out. +## Attempts to disallow adding another set of capabilities. +## Does not attempt to disallow adding the listed capabilities to other files, which would be trivial to bypass. +Cmnd_Alias SETCAP_COBALT = /sbin/setcap cap_net_raw\,cap_sys_nice\,cap_ipc_lock=ep *, ! /sbin/setcap cap_net_raw\,cap_sys_nice\,cap_ipc_lock=ep * * +lofarbuild ALL = (root) NOPASSWD: SETCAP_COBALT diff --git a/RTCP/Cobalt/OutputProc/src/GPUProcIO.cc b/RTCP/Cobalt/OutputProc/src/GPUProcIO.cc index 7cb62c7b0c8a38668807462e0ea969a174da67f4..68c9b7b91f30aaa9dad1670ab802d9700dc0423a 100644 --- a/RTCP/Cobalt/OutputProc/src/GPUProcIO.cc +++ b/RTCP/Cobalt/OutputProc/src/GPUProcIO.cc @@ -35,6 +35,7 @@ #include <Common/StringUtil.h> #include <Common/SystemUtil.h> #include <Common/Exceptions.h> +#include <Common/IOPriority.h> #include <MessageBus/ToBus.h> #include <MessageBus/Protocols/TaskFeedbackDataproducts.h> #include <Stream/PortBroker.h> @@ -50,7 +51,6 @@ #include <CoInterface/SelfDestructTimer.h> #include "SubbandWriter.h" #include "OutputThread.h" -#include "IOPriority.h" using namespace LOFAR; using namespace LOFAR::Cobalt; @@ -100,7 +100,12 @@ size_t getMaxRunTime(const Parset &parset) bool process(Stream &controlStream) { bool success(true); + + // obtain the parset but don't wait forever. Note that we do not know whether + // we're running in real time mode or not. + alarm(3600); Parset parset(&controlStream); + alarm(0); string myHostName = myHostname(false); @@ -110,11 +115,11 @@ bool process(Stream &controlStream) */ // Acquire elevated IO and CPU priorities - setIOpriority(); + setIOpriority(true); setRTpriority(); // Prevent swapping of our buffers - lockInMemory(16UL * 1024UL * 1024UL * 1024UL); // limit memory to 16 GB + //lockInMemory(16UL * 1024UL * 1024UL * 1024UL); // limit memory to 16 GB // Deadline for outputProc, in seconds. const time_t outputProcTimeout = @@ -158,6 +163,8 @@ bool process(Stream &controlStream) && file.location.host != "localhost") continue; + LOG_INFO_STR("starting with fileIdx " << fileIdx); + mdLogger.log(mdKeyPrefix + PN_COP_LOCUS_NODE + '[' + lexical_cast<string>(fileIdx) + ']', formatDataPointLocusName(myHostName)); @@ -166,9 +173,13 @@ bool process(Stream &controlStream) SubbandWriter *writer = new SubbandWriter(parset, fileIdx, mdLogger, mdKeyPrefix, logPrefix); subbandWriters.push_back(writer); + + LOG_INFO_STR("done with fileIdx " << fileIdx); } } + map<size_t, SmartPtr<Arena> > arenas; + map<size_t, SmartPtr<Allocator> > allocators; map<size_t, SmartPtr<Pool<TABTranspose::BeamformedData> > > outputPools; TABTranspose::Receiver::CollectorMap collectors; @@ -187,6 +198,8 @@ bool process(Stream &controlStream) mdLogger.log(mdKeyPrefix + PN_COP_LOCUS_NODE + '[' + lexical_cast<string>(allFileIdx) + ']', formatDataPointLocusName(myHostName)); + LOG_INFO_STR("Allocating transpose buffers for " << file.location.filename); + struct ObservationSettings::BeamFormer::StokesSettings &stokes = file.coherent ? parset.settings.beamFormer.coherentSettings : parset.settings.beamFormer.incoherentSettings; @@ -194,14 +207,20 @@ bool process(Stream &controlStream) const size_t nrSubbands = file.lastSubbandIdx - file.firstSubbandIdx; const size_t nrChannels = stokes.nrChannels; const size_t nrSamples = stokes.nrSamples; + const size_t alignment = TABTranspose::BeamformedData::alignment; + const size_t poolSize = 10; + + arenas[fileIdx] = new MallocedArena(poolSize * (MultiDimArray<float,3>::nrElements(boost::extents[nrSamples][nrSubbands][nrChannels]) * sizeof(float) + alignment), alignment); + allocators[fileIdx] = new SparseSetAllocator(*arenas[fileIdx]); outputPools[fileIdx] = new Pool<TABTranspose::BeamformedData>(str(format("process::outputPool [file %u]") % fileIdx), parset.settings.realTime); // Create and fill an outputPool for this fileIdx - for (size_t i = 0; i < 10; ++i) { + for (size_t i = 0; i < poolSize; ++i) { outputPools[fileIdx]->free.append(new TABTranspose::BeamformedData( boost::extents[nrSamples][nrSubbands][nrChannels], - boost::extents[nrSubbands][nrChannels] + boost::extents[nrSubbands][nrChannels], + *allocators[fileIdx] ), false); } @@ -212,11 +231,15 @@ bool process(Stream &controlStream) string logPrefix = str(format("[obs %u beamformed stream %3u] ") % parset.settings.observationID % fileIdx); + LOG_INFO_STR("Setting up writer for " << file.location.filename); + TABOutputThread *writer = new TABOutputThread(parset, fileIdx, *outputPools[fileIdx], mdLogger, mdKeyPrefix, logPrefix); tabWriters.push_back(writer); } } + LOG_INFO_STR("Finished setting up writers"); + /* * PROCESS */ diff --git a/RTCP/Cobalt/OutputProc/src/MSWriter.cc b/RTCP/Cobalt/OutputProc/src/MSWriter.cc index 1c354398df6d4ba919d0c6c8fa3dcc69f0a58192..8880d7a15fbf6ea1efe3867f6f5549f86cceb739 100644 --- a/RTCP/Cobalt/OutputProc/src/MSWriter.cc +++ b/RTCP/Cobalt/OutputProc/src/MSWriter.cc @@ -1,4 +1,4 @@ -//# MSMriter.cc: Base classs for MS writer +//# MSMriter.cc: Base class for MS writer //# Copyright (C) 2008-2013 ASTRON (Netherlands Institute for Radio Astronomy) //# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands //# diff --git a/RTCP/Cobalt/OutputProc/src/SubbandWriter.cc b/RTCP/Cobalt/OutputProc/src/SubbandWriter.cc index d2a1e12dda56c8c844b58f257d541c2580c9e780..19847a4a0e910f3b4a4a918537102596d4d6802c 100644 --- a/RTCP/Cobalt/OutputProc/src/SubbandWriter.cc +++ b/RTCP/Cobalt/OutputProc/src/SubbandWriter.cc @@ -23,7 +23,9 @@ #include "SubbandWriter.h" #include <CoInterface/CorrelatedData.h> +#include <CoInterface/Allocator.h> #include <CoInterface/OMPThread.h> +#include <Common/Timer.h> #include <boost/format.hpp> using boost::format; @@ -37,19 +39,46 @@ namespace LOFAR const std::string &logPrefix) : itsStreamNr(streamNr), + itsArena(0), + itsAllocator(0), itsOutputPool(str(format("SubbandWriter::itsOutputPool [stream %u]") % streamNr), parset.settings.realTime), itsInputThread(parset, streamNr, itsOutputPool, logPrefix), - itsOutputThread(parset, streamNr, itsOutputPool, mdLogger, mdKeyPrefix, logPrefix) + itsOutputThread(parset, streamNr, itsOutputPool, mdLogger, mdKeyPrefix, logPrefix), + itsAlignment(512), + itsNrStations(parset.settings.correlator.stations.size()), + itsNrChannels(parset.settings.correlator.nrChannels), + itsNrSamples(parset.settings.correlator.nrSamplesPerIntegration()) { - for (unsigned i = 0; i < maxReceiveQueueSize; i++) - itsOutputPool.free.append(new CorrelatedData(parset.settings.correlator.stations.size(), parset.settings.correlator.nrChannels, parset.settings.correlator.nrSamplesPerIntegration(), heapAllocator, 512)); + ASSERT(preAllocateReceiveQueue <= maxReceiveQueueSize); + + // We alloc all memory at once to avoid maxReceiveQueueSize malloc() calls, which occasionally stall on CEP4 + itsArena = new MallocedArena(maxReceiveQueueSize * CorrelatedData::size(itsNrStations, itsNrChannels, itsNrSamples, itsAlignment), itsAlignment); + + itsAllocator = new SparseSetAllocator(*itsArena); + + for (unsigned i = 0; i < preAllocateReceiveQueue; i++) { + CorrelatedData *data = new CorrelatedData(itsNrStations, itsNrChannels, itsNrSamples, *itsAllocator, itsAlignment); + itsOutputPool.free.append(data); + } } void SubbandWriter::process() { -# pragma omp parallel sections num_threads(2) +# pragma omp parallel sections num_threads(3) { +# pragma omp section + { + OMPThread::ScopedName sn(str(format("allocator %u") % itsStreamNr)); + NSTimer timer(str(format("allocator %u") % itsStreamNr), true, true); + + for (unsigned i = preAllocateReceiveQueue; i < maxReceiveQueueSize; i++) { + LOG_INFO_STR(str(format("[stream %u] Allocating element %u") % itsStreamNr % i)); + CorrelatedData *data = new CorrelatedData(itsNrStations, itsNrChannels, itsNrSamples, *itsAllocator, itsAlignment); + itsOutputPool.free.append(data); + } + } + # pragma omp section { OMPThread::ScopedName sn(str(format("input %u") % itsStreamNr)); diff --git a/RTCP/Cobalt/OutputProc/src/SubbandWriter.h b/RTCP/Cobalt/OutputProc/src/SubbandWriter.h index cc3b43767cb5919a5ba5ca266051d887dc532339..c65240167deecc61d7e7ff41e87b68a3dd1719ce 100644 --- a/RTCP/Cobalt/OutputProc/src/SubbandWriter.h +++ b/RTCP/Cobalt/OutputProc/src/SubbandWriter.h @@ -26,6 +26,7 @@ #include <CoInterface/OutputTypes.h> #include <CoInterface/Parset.h> #include <CoInterface/Pool.h> +#include <CoInterface/Allocator.h> #include <CoInterface/SmartPtr.h> #include <CoInterface/StreamableData.h> #include <CoInterface/FinalMetaData.h> @@ -61,14 +62,19 @@ namespace LOFAR unsigned streamNr() const { return itsStreamNr; } private: - static const unsigned maxReceiveQueueSize = 60; + static const unsigned preAllocateReceiveQueue = 4; // number of elements to construct before starting + static const unsigned maxReceiveQueueSize = 60; // number of elements desired in the queue const unsigned itsStreamNr; + SmartPtr<Arena> itsArena; + SmartPtr<Allocator> itsAllocator; Pool<StreamableData> itsOutputPool; InputThread itsInputThread; SubbandOutputThread itsOutputThread; + + const unsigned itsAlignment, itsNrStations, itsNrChannels, itsNrSamples; }; } // namespace Cobalt } // namespace LOFAR diff --git a/RTCP/Cobalt/OutputProc/src/TBB_Writer_main.cc b/RTCP/Cobalt/OutputProc/src/TBB_Writer_main.cc index dce414512d625aba726cf02084e5ddb265f14bfc..c6e99cd0c8ac7f09f13235965585c1aef963418d 100644 --- a/RTCP/Cobalt/OutputProc/src/TBB_Writer_main.cc +++ b/RTCP/Cobalt/OutputProc/src/TBB_Writer_main.cc @@ -40,6 +40,7 @@ #include <Common/LofarLogger.h> #include <Common/StringUtil.h> +#include <Common/IOPriority.h> #include <ApplCommon/StationConfig.h> #include <ApplCommon/AntField.h> #include <CoInterface/Exceptions.h> @@ -51,7 +52,6 @@ #endif #include "TBB_Writer.h" -#include "IOPriority.h" #define TBB_DEFAULT_BASE_PORT 0x7bb0 // i.e. tbb0 #define TBB_DEFAULT_LAST_PORT 0x7bbb // 0x7bbf for NL, 0x7bbb for int'l stations @@ -69,7 +69,7 @@ struct progArgs { bool keepRunning; }; -static volatile sig_atomic_t sigint_seen; +static volatile std::sig_atomic_t sigint_seen; static void termSigsHandler(int sig_nr) { @@ -311,7 +311,7 @@ static void printUsage(const char* progname) { cout << "TBB_Writer version: " << LOFAR::OutputProcVersion::getVersion() << " r" << LOFAR::OutputProcVersion::getRevision() << endl; cout << "Write incoming LOFAR Transient Buffer Board (TBB) data with meta data to storage in HDF5 format." << endl; - cout << "Usage: " << progname << " -p parsets/L12345.parset [OPTION]..." << endl; + cout << "Usage: " << progname << " -s parsets/L12345.parset [OPTION]..." << endl; cout << endl; cout << "Options:" << endl; cout << " -s, --parset=L12345.parset path to file with observation settings (mandatory)" << endl; @@ -516,9 +516,9 @@ int main(int argc, char* argv[]) // We don't run alone, so try to increase the QoS we get from the OS to decrease the chance of data loss. if (parset.settings.realTime) { - setIOpriority(); // reqs CAP_SYS_NICE - setRTpriority(); // reqs CAP_SYS_ADMIN - lockInMemory(); // reqs CAP_IPC_LOCK + LOFAR::setIOpriority(true); // reqs CAP_SYS_ADMIN iff passing realTime is true + LOFAR::setRTpriority(); // reqs CAP_SYS_NICE + LOFAR::lockInMemory(); // reqs CAP_IPC_LOCK } LOFAR::Cobalt::StationMetaDataMap stMdMap(getExternalStationMetaData(parset, args.staticMetaDataDir)); diff --git a/RTCP/Cobalt/OutputProc/test/tSubbandWriter.cc b/RTCP/Cobalt/OutputProc/test/tSubbandWriter.cc index 24eee255d26f1177dfccb76dfae7db337de33384..d51f882ab7177d1daa52a45c53155b1a2a40c906 100644 --- a/RTCP/Cobalt/OutputProc/test/tSubbandWriter.cc +++ b/RTCP/Cobalt/OutputProc/test/tSubbandWriter.cc @@ -57,15 +57,21 @@ SUITE(SubbandWriter) Parset ps; OneBeam() { - ps.add("Observation.VirtualInstrument.stationList", "[CS001]"); + ps.add("Observation.VirtualInstrument.stationList", "[CS001,15*CS002]"); ps.add("Observation.Dataslots.CS001LBA.RSPBoardList", "[0]"); ps.add("Observation.Dataslots.CS001LBA.DataslotList", "[0]"); + ps.add("Observation.Dataslots.CS002LBA.RSPBoardList", "[0]"); + ps.add("Observation.Dataslots.CS002LBA.DataslotList", "[0]"); ps.add("PIC.Core.CS001LBA.position", "[0,0,0]"); ps.add("PIC.Core.CS001LBA.phaseCenter", "[0,0,0]"); + ps.add("PIC.Core.CS002LBA.position", "[0,0,0]"); + ps.add("PIC.Core.CS002LBA.phaseCenter", "[0,0,0]"); ps.add("Observation.ObsID", boost::lexical_cast<string>(obsId)); ps.add("Observation.startTime", "2013-01-01 00:00"); ps.add("Observation.stopTime", "2013-01-01 01:00"); + ps.add("Cobalt.Correlator.nrChannelsPerSubband", "64"); + ps.add("Cobalt.blockSize", "196608"); ps.add("Observation.nrBeams", "1"); ps.add("Observation.Beam[0].subbandList", "[0]"); diff --git a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj b/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj deleted file mode 100644 index f017fd5a9b312748afcd2a597e7c477260e28eda..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj +++ /dev/null @@ -1,142 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\CoInterface\src\Align.h" /> - <ClInclude Include="..\..\CoInterface\src\Allocator.h" /> - <ClInclude Include="..\..\CoInterface\src\BeamCoordinates.h" /> - <ClInclude Include="..\..\CoInterface\src\BeamFormedData.h" /> - <ClInclude Include="..\..\CoInterface\src\BestEffortQueue.h" /> - <ClInclude Include="..\..\CoInterface\src\BlockID.h" /> - <ClInclude Include="..\..\CoInterface\src\Config.h" /> - <ClInclude Include="..\..\CoInterface\src\CorrelatedData.h" /> - <ClInclude Include="..\..\CoInterface\src\DataFactory.h" /> - <ClInclude Include="..\..\CoInterface\src\Exceptions.h" /> - <ClInclude Include="..\..\CoInterface\src\FinalMetaData.h" /> - <ClInclude Include="..\..\CoInterface\src\InverseFilteredData.h" /> - <ClInclude Include="..\..\CoInterface\src\MultiDimArray.h" /> - <ClInclude Include="..\..\CoInterface\src\OutputTypes.h" /> - <ClInclude Include="..\..\CoInterface\src\Parset.h" /> - <ClInclude Include="..\..\CoInterface\src\Poll.h" /> - <ClInclude Include="..\..\CoInterface\src\Pool.h" /> - <ClInclude Include="..\..\CoInterface\src\PrintVector.h" /> - <ClInclude Include="..\..\CoInterface\src\RingCoordinates.h" /> - <ClInclude Include="..\..\CoInterface\src\SetOperations.h" /> - <ClInclude Include="..\..\CoInterface\src\SlidingPointer.h" /> - <ClInclude Include="..\..\CoInterface\src\SmartPtr.h" /> - <ClInclude Include="..\..\CoInterface\src\SparseSet.h" /> - <ClInclude Include="..\..\CoInterface\src\Stream.h" /> - <ClInclude Include="..\..\CoInterface\src\StreamableData.h" /> - <ClInclude Include="..\..\CoInterface\src\SubbandMetaData.h" /> - <ClInclude Include="..\..\CoInterface\src\TABTranspose.h" /> - <ClInclude Include="..\..\CoInterface\src\TriggerData.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\CoInterface\src\Allocator.cc" /> - <ClCompile Include="..\..\CoInterface\src\BeamCoordinates.cc" /> - <ClCompile Include="..\..\CoInterface\src\BlockID.cc" /> - <ClCompile Include="..\..\CoInterface\src\CorrelatedData.cc" /> - <ClCompile Include="..\..\CoInterface\src\DataFactory.cc" /> - <ClCompile Include="..\..\CoInterface\src\FinalMetaData.cc" /> - <ClCompile Include="..\..\CoInterface\src\Parset.cc" /> - <ClCompile Include="..\..\CoInterface\src\RingCoordinates.cc" /> - <ClCompile Include="..\..\CoInterface\src\Stream.cc" /> - <ClCompile Include="..\..\CoInterface\src\TABTranspose.cc" /> - <ClCompile Include="..\..\CoInterface\test\tBestEffortQueue.cc" /> - <ClCompile Include="..\..\CoInterface\test\tCorrelatedData.cc" /> - <ClCompile Include="..\..\CoInterface\test\tMultiDimArray.cc" /> - <ClCompile Include="..\..\CoInterface\test\tParset.cc" /> - <ClCompile Include="..\..\CoInterface\test\tpow2.cc" /> - <ClCompile Include="..\..\CoInterface\test\tRingCoordinates.cc" /> - <ClCompile Include="..\..\CoInterface\test\tSparseSet.cc" /> - <ClCompile Include="..\..\CoInterface\test\tTABTranspose.cc" /> - </ItemGroup> - <ItemGroup> - <None Include="..\..\CoInterface\src\BestEffortQueue.tcc" /> - <None Include="..\..\CoInterface\test\tParset.parset_obs99275" /> - <None Include="..\..\CoInterface\test\tParset.sh" /> - <None Include="..\..\CoInterface\test\tRingCoordinates.py" /> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\CoInterface\src\CMakeLists.txt" /> - <Text Include="..\..\CoInterface\test\CMakeLists.txt" /> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>CoInterface</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;..\..\..\static_fixture;..\..\;..\symbolic_links;C:\Program Files (x86)\boost\boost_1_41</IncludePath> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.filters b/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.filters deleted file mode 100644 index 860a9e32347998b1b03272cfb8f76bdb56d5ca5e..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.filters +++ /dev/null @@ -1,175 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="src"> - <UniqueIdentifier>{3503dac2-4fb3-4419-97c0-825dd34791f4}</UniqueIdentifier> - </Filter> - <Filter Include="test"> - <UniqueIdentifier>{20382d69-318e-4746-bd79-98c7cb7b6c7f}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\CoInterface\src\Poll.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Pool.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\PrintVector.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\SetOperations.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\SlidingPointer.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\SmartPtr.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\SparseSet.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Stream.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\StreamableData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\SubbandMetaData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\TABTranspose.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\TriggerData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Align.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Allocator.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\BeamCoordinates.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\BeamFormedData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\BestEffortQueue.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\BlockID.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Config.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\CorrelatedData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\DataFactory.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Exceptions.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\FinalMetaData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\InverseFilteredData.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\MultiDimArray.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\OutputTypes.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\Parset.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\CoInterface\src\RingCoordinates.h"> - <Filter>src</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\CoInterface\src\Stream.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\TABTranspose.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\Allocator.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\BeamCoordinates.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\BlockID.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\CorrelatedData.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\DataFactory.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\FinalMetaData.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\Parset.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\src\RingCoordinates.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tParset.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tpow2.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tRingCoordinates.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tSparseSet.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tTABTranspose.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tBestEffortQueue.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tCorrelatedData.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\CoInterface\test\tMultiDimArray.cc"> - <Filter>test</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="..\..\CoInterface\src\BestEffortQueue.tcc"> - <Filter>src</Filter> - </None> - <None Include="..\..\CoInterface\test\tParset.parset_obs99275"> - <Filter>test</Filter> - </None> - <None Include="..\..\CoInterface\test\tParset.sh"> - <Filter>test</Filter> - </None> - <None Include="..\..\CoInterface\test\tRingCoordinates.py"> - <Filter>test</Filter> - </None> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\CoInterface\src\CMakeLists.txt"> - <Filter>src</Filter> - </Text> - <Text Include="..\..\CoInterface\test\CMakeLists.txt"> - <Filter>test</Filter> - </Text> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.user b/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/CoInterface/CoInterface.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.sln b/RTCP/Cobalt/VisualStudio/Cobalt.sln deleted file mode 100644 index d4c8e81a04da6747bff7da480edb96b3b3be0e0b..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/Cobalt.sln +++ /dev/null @@ -1,44 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoInterface", "CoInterface\CoInterface.vcxproj", "{0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPUProc", "GPUProc\GPUProc.vcxproj", "{93C5C054-7BF7-478E-A8B5-E0829597AEEA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InputProc", "InputProc\InputProc.vcxproj", "{075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OutputProc", "OutputProc\OutputProc.vcxproj", "{AC4EED56-6048-4D80-8350-F5A753238CC3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{1521604F-7418-4628-932D-40ED94A1E68E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}.Debug|Win32.ActiveCfg = Debug|Win32 - {0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}.Debug|Win32.Build.0 = Debug|Win32 - {0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}.Release|Win32.ActiveCfg = Release|Win32 - {0C6B2AFD-61F8-4E27-BDB8-FECDA6DC04CE}.Release|Win32.Build.0 = Release|Win32 - {93C5C054-7BF7-478E-A8B5-E0829597AEEA}.Debug|Win32.ActiveCfg = Debug|Win32 - {93C5C054-7BF7-478E-A8B5-E0829597AEEA}.Debug|Win32.Build.0 = Debug|Win32 - {93C5C054-7BF7-478E-A8B5-E0829597AEEA}.Release|Win32.ActiveCfg = Release|Win32 - {93C5C054-7BF7-478E-A8B5-E0829597AEEA}.Release|Win32.Build.0 = Release|Win32 - {075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}.Debug|Win32.ActiveCfg = Debug|Win32 - {075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}.Debug|Win32.Build.0 = Debug|Win32 - {075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}.Release|Win32.ActiveCfg = Release|Win32 - {075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}.Release|Win32.Build.0 = Release|Win32 - {AC4EED56-6048-4D80-8350-F5A753238CC3}.Debug|Win32.ActiveCfg = Debug|Win32 - {AC4EED56-6048-4D80-8350-F5A753238CC3}.Debug|Win32.Build.0 = Debug|Win32 - {AC4EED56-6048-4D80-8350-F5A753238CC3}.Release|Win32.ActiveCfg = Release|Win32 - {AC4EED56-6048-4D80-8350-F5A753238CC3}.Release|Win32.Build.0 = Release|Win32 - {1521604F-7418-4628-932D-40ED94A1E68E}.Debug|Win32.ActiveCfg = Debug|Win32 - {1521604F-7418-4628-932D-40ED94A1E68E}.Debug|Win32.Build.0 = Debug|Win32 - {1521604F-7418-4628-932D-40ED94A1E68E}.Release|Win32.ActiveCfg = Release|Win32 - {1521604F-7418-4628-932D-40ED94A1E68E}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.suo b/RTCP/Cobalt/VisualStudio/Cobalt.suo deleted file mode 100644 index 299320f10e8dd2d392b13f4be78f7b94d1c29f3b..0000000000000000000000000000000000000000 Binary files a/RTCP/Cobalt/VisualStudio/Cobalt.suo and /dev/null differ diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.v12.suo b/RTCP/Cobalt/VisualStudio/Cobalt.v12.suo deleted file mode 100644 index 404858a92a6f30be2ab9ca0b84a16404a8ae6984..0000000000000000000000000000000000000000 Binary files a/RTCP/Cobalt/VisualStudio/Cobalt.v12.suo and /dev/null differ diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj b/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj deleted file mode 100644 index 59dc452569dfef40df2e8a2a6e020739d407895a..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{8AD5FFB5-58CA-4A1F-8519-A8401156EF54}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>Cobalt</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.filters b/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.filters deleted file mode 100644 index 47cfb34efaef9914ff297c8c352a631c6bd4c483..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.filters +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.user b/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/Cobalt.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj b/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj deleted file mode 100644 index a70638c7d2bd4d52d42e20d942e6e0024c7d3fb8..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj +++ /dev/null @@ -1,422 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\GPUProc\src\BandPass.h" /> - <ClInclude Include="..\..\GPUProc\src\cpu_utils.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\gpu_incl.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\gpu_wrapper.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\KernelFactory.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BandPassCorrectionKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CorrelatorKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionBackwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionChirpKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionForwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DelayAndBandPassKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FFT_Plan.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\Filter_FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FIR_FilterKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IntToFloatKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFIR_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TriggerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\MultiDimArrayHostBuffer.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\PerformanceCounter.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\BeamFormerPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\CorrelatorPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\Pipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\UHEP_Pipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerCoherentStep.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerFactories.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerIncoherentStep.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerPreprocessingStep.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProc.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProcStep.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\CorrelatorSubbandProc.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\SubbandProc.h" /> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\UHEP_SubbandProc.h" /> - <ClInclude Include="..\..\GPUProc\src\FilterBank.h" /> - <ClInclude Include="..\..\GPUProc\src\global_defines.h" /> - <ClInclude Include="..\..\GPUProc\src\gpu_incl.h" /> - <ClInclude Include="..\..\GPUProc\src\gpu_utils.h" /> - <ClInclude Include="..\..\GPUProc\src\gpu_wrapper.h" /> - <ClInclude Include="..\..\GPUProc\src\KernelFactory.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\BeamFormerTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\CoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\CoherentStokesTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\CorrelatorKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionBackwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionChirpKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionForwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\DelayAndBandPassKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\Filter_FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\FIR_FilterKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\IncoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\IntToFloatKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_InvFFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_InvFIR_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_TransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_TriggerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\MPI_utils.h" /> - <ClInclude Include="..\..\GPUProc\src\MultiDimArrayHostBuffer.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_incl.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_utils.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_wrapper.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerTransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\CoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\CorrelatorKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionBackwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionChirpKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionForwardFFTkernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DelayAndBandPassKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FFT_Plan.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\Filter_FFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FIR_FilterKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\IncoherentStokesKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\IntToFloatKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_BeamFormerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFFT_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFIR_Kernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TransposeKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TriggerKernel.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\MultiDimArrayHostBuffer.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\PerformanceCounter.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\BeamFormerPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipelinePrograms.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\Pipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\UHEP_Pipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\BeamFormerWorkQueue.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\CorrelatorWorkQueue.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\UHEP_WorkQueue.h" /> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\WorkQueue.h" /> - <ClInclude Include="..\..\GPUProc\src\OpenMP_Lock.h" /> - <ClInclude Include="..\..\GPUProc\src\PerformanceCounter.h" /> - <ClInclude Include="..\..\GPUProc\src\Pipelines\BeamFormerPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\Pipelines\CorrelatorPipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\Pipelines\CorrelatorPipelinePrograms.h" /> - <ClInclude Include="..\..\GPUProc\src\Pipelines\UHEP_Pipeline.h" /> - <ClInclude Include="..\..\GPUProc\src\RunningStatistics.h" /> - <ClInclude Include="..\..\GPUProc\src\Station\StationInput.h" /> - <ClInclude Include="..\..\GPUProc\src\Station\StationNodeAllocation.h" /> - <ClInclude Include="..\..\GPUProc\src\Storage\SSH.h" /> - <ClInclude Include="..\..\GPUProc\src\Storage\StorageProcess.h" /> - <ClInclude Include="..\..\GPUProc\src\Storage\StorageProcesses.h" /> - <ClInclude Include="..\..\GPUProc\test\cuda\tFIR_Filter.h" /> - <ClInclude Include="..\..\GPUProc\test\Kernels\KernelTestHelpers.h" /> - <ClInclude Include="..\..\GPUProc\test\TestUtil.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\GPUProc\src\BandPass.cc" /> - <ClCompile Include="..\..\GPUProc\src\cpu_utils.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\gpu_utils.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\gpu_wrapper.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\KernelFactory.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BandPassCorrectionKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerTransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesTransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CorrelatorKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionBackwardFFTkernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionChirpKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionForwardFFTkernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DelayAndBandPassKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFTShiftKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFT_Plan.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\Filter_FFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FIR_FilterKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesTransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IntToFloatKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_BeamFormerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFIR_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TriggerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\PerformanceCounter.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\BeamFormerPipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\CorrelatorPipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\Pipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\UHEP_Pipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerCoherentStep.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerFactories.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerIncoherentStep.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerPreprocessingStep.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\CorrelatorSubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\SubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\UHEP_SubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\src\FilterBank.cc" /> - <ClCompile Include="..\..\GPUProc\src\global_defines.cc" /> - <ClCompile Include="..\..\GPUProc\src\MPI_utils.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\gpu_utils.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\gpu_wrapper.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerTransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\CoherentStokesKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\CorrelatorKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionBackwardFFTkernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionChirpKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionForwardFFTkernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DelayAndBandPassKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FFT_Plan.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\Filter_FFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FIR_FilterKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\IncoherentStokesKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\IntToFloatKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_BeamFormerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFIR_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TransposeKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TriggerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\PerformanceCounter.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\BeamFormerPipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\Pipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\UHEP_Pipeline.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\BeamFormerWorkQueue.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\CorrelatorWorkQueue.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\UHEP_WorkQueue.cc" /> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\WorkQueue.cc" /> - <ClCompile Include="..\..\GPUProc\src\rtcp.cc" /> - <ClCompile Include="..\..\GPUProc\src\RunningStatistics.cc" /> - <ClCompile Include="..\..\GPUProc\src\Station\StationInput.cc" /> - <ClCompile Include="..\..\GPUProc\src\Station\StationNodeAllocation.cc" /> - <ClCompile Include="..\..\GPUProc\src\Storage\SSH.cc" /> - <ClCompile Include="..\..\GPUProc\src\Storage\StorageProcess.cc" /> - <ClCompile Include="..\..\GPUProc\src\Storage\StorageProcesses.cc" /> - <ClCompile Include="..\..\GPUProc\test\cmpfloat.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tBandPassCorrection.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tBeamFormer.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tCoherentStokes.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tCoherentStokesTranspose.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tCorrelator.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tFFT.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tFFT_leakage.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tFIR_Filter.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tGPUWrapper.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tIntToFloat.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tMultiDimArrayHostBuffer.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tStreamReadBuffer.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tTranspose.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\BeamFormerKernelPerformance.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\KernelTestHelpers.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tFIR_FilterKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.cc" /> - <ClCompile Include="..\..\GPUProc\test\Kernels\tKernelFunctions.cc" /> - <ClCompile Include="..\..\GPUProc\test\Pipelines\tCorrelatorPipelineProcessObs.cc" /> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.cc" /> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.cc" /> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.cc" /> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tSubbandProc.cc" /> - <ClCompile Include="..\..\GPUProc\test\tBandPass.cc" /> - <ClCompile Include="..\..\GPUProc\test\tMPISendReceive.cc" /> - <ClCompile Include="..\..\GPUProc\test\tSSH.cc" /> - <ClCompile Include="..\..\GPUProc\test\tStationInput.cc" /> - <ClCompile Include="..\..\GPUProc\test\t_cpu_utils.cc" /> - <ClCompile Include="..\..\GPUProc\test\t_gpu_utils.cc" /> - </ItemGroup> - <ItemGroup> - <None Include="..\..\GPUProc\share\gpu\kernels\BandPassCorrection.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\BeamFormer.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\CoherentStokes.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\CoherentStokesTranspose.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\Correlator.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\DelayAndBandPass.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\FIR_Filter.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\gpu_math.cuh" /> - <None Include="..\..\GPUProc\share\gpu\kernels\IncoherentStokes.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\IncoherentStokesTranspose.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\IntToFloat.cu" /> - <None Include="..\..\GPUProc\share\gpu\kernels\IntToFloat.cuh" /> - <None Include="..\..\GPUProc\share\gpu\kernels\Transpose.cu" /> - <None Include="..\..\GPUProc\src\cuda\CMakeLists.txt" /> - <None Include="..\..\GPUProc\src\cuda\cuda_config.h.in" /> - <None Include="..\..\GPUProc\src\cuda\gpu_wrapper.tcc" /> - <None Include="..\..\GPUProc\src\opencl\CMakeLists.txt" /> - <None Include="..\..\GPUProc\src\opencl\Correlator.cl" /> - <None Include="..\..\GPUProc\src\opencl\DelayAndBandPass.cl" /> - <None Include="..\..\GPUProc\src\opencl\FFT.cl" /> - <None Include="..\..\GPUProc\src\opencl\fft2.cl" /> - <None Include="..\..\GPUProc\src\opencl\FIR.cl" /> - <None Include="..\..\GPUProc\src\opencl\math.cl" /> - <None Include="..\..\GPUProc\src\opencl\NewCorrelator.cl" /> - <None Include="..\..\GPUProc\src\rtcp.log_prop" /> - <None Include="..\..\GPUProc\test\cmpfloat.py" /> - <None Include="..\..\GPUProc\test\cuda\CMakeLists.txt" /> - <None Include="..\..\GPUProc\test\cuda\tBandPassCorrection.sh" /> - <None Include="..\..\GPUProc\test\cuda\tBeamFormer.sh" /> - <None Include="..\..\GPUProc\test\cuda\tCoherentStokes.sh" /> - <None Include="..\..\GPUProc\test\cuda\tCoherentStokesTranspose.sh" /> - <None Include="..\..\GPUProc\test\cuda\tCorrelator.sh" /> - <None Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.sh" /> - <None Include="..\..\GPUProc\test\cuda\tFFT_leakage.in_.parset" /> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.77_Stations" /> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.AARTFAAC" /> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.small-test" /> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.run" /> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.sh" /> - <None Include="..\..\GPUProc\test\cuda\tIntToFloat.sh" /> - <None Include="..\..\GPUProc\test\cuda\tKernel.in_.cu" /> - <None Include="..\..\GPUProc\test\cuda\tKernel.parset.in" /> - <None Include="..\..\GPUProc\test\cuda\tKernel.sh" /> - <None Include="..\..\GPUProc\test\cuda\tMultiDimArrayHostBuffer.sh" /> - <None Include="..\..\GPUProc\test\cuda\tStreamReadBuffer.sh" /> - <None Include="..\..\GPUProc\test\cuda\tTranspose.sh" /> - <None Include="..\..\GPUProc\test\cuda\tTranspose2.sh" /> - <None Include="..\..\GPUProc\test\cuda\t_cuda_complex.cu" /> - <None Include="..\..\GPUProc\test\cuda\t_cuda_complex.sh" /> - <None Include="..\..\GPUProc\test\cuda\Vizualize_leakage.py" /> - <None Include="..\..\GPUProc\test\iperf-cbt-locus.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tFIR_FilterKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.in_parset" /> - <None Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tKernelFunctions.sh" /> - <None Include="..\..\GPUProc\test\Kernels\tKernelPerformance.py" /> - <None Include="..\..\GPUProc\test\Kernels\visualizeBeamformer.py" /> - <None Include="..\..\GPUProc\test\Pipelines\tCorrelatorPipelineProcessObs.parset" /> - <None Include="..\..\GPUProc\test\Pipelines\tCorrelatorPipelineProcessObs.sh" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.parset" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.sh" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.parset" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.sh" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.parset" /> - <None Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.sh" /> - <None Include="..\..\GPUProc\test\tMPISendReceive.in_parset" /> - <None Include="..\..\GPUProc\test\tMPISendReceive.sh" /> - <None Include="..\..\GPUProc\test\t_cpu_utils.in_parset" /> - <None Include="..\..\GPUProc\test\t_cpu_utils.sh" /> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\GPUProc\share\gpu\kernels\CMakeLists.txt" /> - <Text Include="..\..\GPUProc\src\CMakeLists.txt" /> - <Text Include="..\..\GPUProc\test\CMakeLists.txt" /> - <Text Include="..\..\GPUProc\test\Kernels\CMakeLists.txt" /> - <Text Include="..\..\GPUProc\test\Pipelines\CMakeLists.txt" /> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{93C5C054-7BF7-478E-A8B5-E0829597AEEA}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>GPUProc</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;..\..\..\static_fixture;..\..\;..\symbolic_links;C:\Program Files (x86)\boost\boost_1_41;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v5.5\include;D:\Users\klijn\Downloads\boost_1_55_0\boost_1_55_0\boost</IncludePath> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader>Use</PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);USE_CUDA; NR_COHERENT_STOKES=4</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader>Use</PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.filters b/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.filters deleted file mode 100644 index 6c0532571aa3b963c026f854cb4c578b408fb3e2..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.filters +++ /dev/null @@ -1,1042 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="src"> - <UniqueIdentifier>{52b27d78-7e52-4da5-96f4-7ec7aacebf9f}</UniqueIdentifier> - </Filter> - <Filter Include="src\cuda"> - <UniqueIdentifier>{6d5d776e-f435-4271-ba8a-08cfe01119ad}</UniqueIdentifier> - </Filter> - <Filter Include="src\cuda\Kernels"> - <UniqueIdentifier>{a4f44168-d60e-4eea-b9a2-263bf0cb7b57}</UniqueIdentifier> - </Filter> - <Filter Include="src\cuda\Pipelines"> - <UniqueIdentifier>{050b51f0-c906-4fc1-8f40-48ce89066bba}</UniqueIdentifier> - </Filter> - <Filter Include="src\cuda\SubbandProcs"> - <UniqueIdentifier>{b04db8d2-92c1-4a96-93fb-6f94cd4bb17a}</UniqueIdentifier> - </Filter> - <Filter Include="src\Pipelines"> - <UniqueIdentifier>{30105c21-2cdf-461e-8738-7f43e4c608c0}</UniqueIdentifier> - </Filter> - <Filter Include="src\Kernels"> - <UniqueIdentifier>{8a81bd65-646d-43ba-ac7b-5beb99de019c}</UniqueIdentifier> - </Filter> - <Filter Include="src\Station"> - <UniqueIdentifier>{531671df-db5c-4132-8255-ef0970f07246}</UniqueIdentifier> - </Filter> - <Filter Include="src\Storage"> - <UniqueIdentifier>{f0428178-4eef-4140-8af8-178e4c927400}</UniqueIdentifier> - </Filter> - <Filter Include="src\SubbandProcs"> - <UniqueIdentifier>{4189daf6-f0e0-4bb1-8bd6-0d96bbc218d2}</UniqueIdentifier> - </Filter> - <Filter Include="test"> - <UniqueIdentifier>{91363a0a-dbf1-43e9-a345-cc3e6fe4fa1f}</UniqueIdentifier> - </Filter> - <Filter Include="test\cuda"> - <UniqueIdentifier>{51b333f8-a7d1-4342-9046-a9e5a8e71783}</UniqueIdentifier> - </Filter> - <Filter Include="share"> - <UniqueIdentifier>{6366e12c-9513-4476-af75-1c5f0e9a6295}</UniqueIdentifier> - </Filter> - <Filter Include="src\opencl"> - <UniqueIdentifier>{df0cf1e4-c813-41cb-b576-495868d16308}</UniqueIdentifier> - </Filter> - <Filter Include="src\opencl\Pipelines"> - <UniqueIdentifier>{d4c8e2db-d252-4de7-8587-e22411d36bbf}</UniqueIdentifier> - </Filter> - <Filter Include="src\opencl\WorkQueues"> - <UniqueIdentifier>{4022e73c-c37c-44ff-a0e3-8345663f831e}</UniqueIdentifier> - </Filter> - <Filter Include="src\opencl\Kernels"> - <UniqueIdentifier>{773522c4-e9e7-4c46-be83-2ba0cf75ac54}</UniqueIdentifier> - </Filter> - <Filter Include="test\Kernels"> - <UniqueIdentifier>{d12b2f89-11b7-4abe-b8d3-b244619f1bc9}</UniqueIdentifier> - </Filter> - <Filter Include="test\SubbandProcs"> - <UniqueIdentifier>{0ddb3aaa-b339-4fc9-933d-51ab12f2f419}</UniqueIdentifier> - </Filter> - <Filter Include="src\cuda\SubbandProcs\steps"> - <UniqueIdentifier>{523a5915-d119-4fbc-bf32-f57136137d3e}</UniqueIdentifier> - </Filter> - <Filter Include="test\Pipelines"> - <UniqueIdentifier>{0b1f82fc-7678-4044-9aab-79b3f9f6d64a}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\BeamFormerPipeline.h"> - <Filter>src\cuda\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\CorrelatorPipeline.h"> - <Filter>src\cuda\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\Pipeline.h"> - <Filter>src\cuda\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Pipelines\UHEP_Pipeline.h"> - <Filter>src\cuda\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProc.h"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\CorrelatorSubbandProc.h"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\SubbandProc.h"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\UHEP_SubbandProc.h"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\gpu_incl.h"> - <Filter>src\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\gpu_wrapper.h"> - <Filter>src\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\KernelFactory.h"> - <Filter>src\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\MultiDimArrayHostBuffer.h"> - <Filter>src\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\PerformanceCounter.h"> - <Filter>src\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Pipelines\BeamFormerPipeline.h"> - <Filter>src\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Pipelines\CorrelatorPipeline.h"> - <Filter>src\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Pipelines\CorrelatorPipelinePrograms.h"> - <Filter>src\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Pipelines\UHEP_Pipeline.h"> - <Filter>src\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\BeamFormerKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\BeamFormerTransposeKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\CoherentStokesKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\CorrelatorKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionBackwardFFTkernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionChirpKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\DedispersionForwardFFTkernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\DelayAndBandPassKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\FFT_Kernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\Filter_FFT_Kernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\FIR_FilterKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\IncoherentStokesKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\IntToFloatKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\Kernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_BeamFormerKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_InvFFT_Kernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_InvFIR_Kernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_TransposeKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\UHEP_TriggerKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Station\StationInput.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Station\StationNodeAllocation.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Storage\StorageProcesses.h"> - <Filter>src\Storage</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Storage\SSH.h"> - <Filter>src\Storage</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Storage\StorageProcess.h"> - <Filter>src\Storage</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\test\cuda\tFIR_Filter.h"> - <Filter>test\cuda</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_incl.h"> - <Filter>src\opencl</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_utils.h"> - <Filter>src\opencl</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\gpu_wrapper.h"> - <Filter>src\opencl</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\MultiDimArrayHostBuffer.h"> - <Filter>src\opencl</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\PerformanceCounter.h"> - <Filter>src\opencl</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\BeamFormerPipeline.h"> - <Filter>src\opencl\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipeline.h"> - <Filter>src\opencl\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipelinePrograms.h"> - <Filter>src\opencl\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\Pipeline.h"> - <Filter>src\opencl\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Pipelines\UHEP_Pipeline.h"> - <Filter>src\opencl\Pipelines</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\BeamFormerWorkQueue.h"> - <Filter>src\opencl\WorkQueues</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\CorrelatorWorkQueue.h"> - <Filter>src\opencl\WorkQueues</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\UHEP_WorkQueue.h"> - <Filter>src\opencl\WorkQueues</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\WorkQueues\WorkQueue.h"> - <Filter>src\opencl\WorkQueues</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FFT_Plan.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\Filter_FFT_Kernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FIR_FilterKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\IncoherentStokesKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\IntToFloatKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\Kernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_BeamFormerKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFFT_Kernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFIR_Kernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TransposeKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TriggerKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerTransposeKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\CoherentStokesKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\CorrelatorKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionBackwardFFTkernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionChirpKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DedispersionForwardFFTkernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\DelayAndBandPassKernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\opencl\Kernels\FFT_Kernel.h"> - <Filter>src\opencl\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\Kernels\CoherentStokesTransposeKernel.h"> - <Filter>src\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\gpu_incl.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\gpu_utils.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\gpu_wrapper.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\KernelFactory.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\MultiDimArrayHostBuffer.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\OpenMP_Lock.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\PerformanceCounter.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\RunningStatistics.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\BandPass.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cpu_utils.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\FilterBank.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\global_defines.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerFactories.h"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesTransposeKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CorrelatorKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionBackwardFFTkernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionChirpKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DedispersionForwardFFTkernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\DelayAndBandPassKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FFT_Kernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FFT_Plan.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\Filter_FFT_Kernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\FIR_FilterKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesTransposeKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\IntToFloatKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\Kernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_BeamFormerKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFFT_Kernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFIR_Kernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TransposeKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TriggerKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerTransposeKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\Kernels\BandPassCorrectionKernel.h"> - <Filter>src\cuda\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProcStep.h"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerPreprocessingStep.h"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerCoherentStep.h"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerIncoherentStep.h"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\test\Kernels\KernelTestHelpers.h"> - <Filter>test\Kernels</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\test\TestUtil.h"> - <Filter>test</Filter> - </ClInclude> - <ClInclude Include="..\..\GPUProc\src\MPI_utils.h"> - <Filter>src</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\BeamFormerPipeline.cc"> - <Filter>src\cuda\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\CorrelatorPipeline.cc"> - <Filter>src\cuda\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\Pipeline.cc"> - <Filter>src\cuda\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Pipelines\UHEP_Pipeline.cc"> - <Filter>src\cuda\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\CorrelatorSubbandProc.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\SubbandProc.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\UHEP_SubbandProc.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerSubbandProc.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\gpu_utils.cc"> - <Filter>src\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\gpu_wrapper.cc"> - <Filter>src\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\KernelFactory.cc"> - <Filter>src\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\PerformanceCounter.cc"> - <Filter>src\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\Station\StationInput.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\Station\StationNodeAllocation.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\Storage\StorageProcesses.cc"> - <Filter>src\Storage</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\Storage\SSH.cc"> - <Filter>src\Storage</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\Storage\StorageProcess.cc"> - <Filter>src\Storage</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tCoherentStokes.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tCorrelator.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tFFT.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tFFT_leakage.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tFIR_Filter.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tGPUWrapper.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tIntToFloat.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tKernel.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tMultiDimArrayHostBuffer.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tStreamReadBuffer.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tTranspose.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tBeamFormer.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\gpu_utils.cc"> - <Filter>src\opencl</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\gpu_wrapper.cc"> - <Filter>src\opencl</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\PerformanceCounter.cc"> - <Filter>src\opencl</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\CorrelatorPipeline.cc"> - <Filter>src\opencl\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\Pipeline.cc"> - <Filter>src\opencl\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\UHEP_Pipeline.cc"> - <Filter>src\opencl\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Pipelines\BeamFormerPipeline.cc"> - <Filter>src\opencl\Pipelines</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\CorrelatorWorkQueue.cc"> - <Filter>src\opencl\WorkQueues</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\UHEP_WorkQueue.cc"> - <Filter>src\opencl\WorkQueues</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\WorkQueue.cc"> - <Filter>src\opencl\WorkQueues</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\WorkQueues\BeamFormerWorkQueue.cc"> - <Filter>src\opencl\WorkQueues</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\Filter_FFT_Kernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FIR_FilterKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\IncoherentStokesKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\IntToFloatKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\Kernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_BeamFormerKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFFT_Kernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_InvFIR_Kernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TransposeKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\UHEP_TriggerKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\BeamFormerTransposeKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\CoherentStokesKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\CorrelatorKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionBackwardFFTkernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionChirpKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DedispersionForwardFFTkernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\DelayAndBandPassKernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FFT_Kernel.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\opencl\Kernels\FFT_Plan.cc"> - <Filter>src\opencl\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tCoherentStokesTranspose.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\rtcp.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\RunningStatistics.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\BandPass.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cpu_utils.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\FilterBank.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\global_defines.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerFactories.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CorrelatorKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionBackwardFFTkernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionChirpKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DedispersionForwardFFTkernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\DelayAndBandPassKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFT_Kernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFT_Plan.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\Filter_FFT_Kernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FIR_FilterKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IncoherentStokesTransposeKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\IntToFloatKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\Kernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_BeamFormerKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFFT_Kernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_InvFIR_Kernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TransposeKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\UHEP_TriggerKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BeamFormerTransposeKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\CoherentStokesTransposeKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\FFTShiftKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tFIR_FilterKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tKernelFunctions.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerFactories.cc"> - <Filter>src\cuda\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProc.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tSubbandProc.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\Kernels\BandPassCorrectionKernel.cc"> - <Filter>src\cuda\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerPreprocessingStep.cc"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerCoherentStep.cc"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\src\cuda\SubbandProcs\BeamFormerIncoherentStep.cc"> - <Filter>src\cuda\SubbandProcs\steps</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\cuda\tBandPassCorrection.cc"> - <Filter>test\cuda</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\BeamFormerKernelPerformance.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\Kernels\KernelTestHelpers.cc"> - <Filter>test\Kernels</Filter> - </ClCompile> - <ClCompile Include="..\..\GPUProc\test\SubbandProcs\tFlysEyeBeamFormerSubbandProcProcessSb.cc"> - <Filter>test\SubbandProcs</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="..\..\GPUProc\src\cuda\CMakeLists.txt"> - <Filter>src\cuda</Filter> - </None> - <None Include="..\..\GPUProc\src\cuda\cuda_config.h.in"> - <Filter>src\cuda</Filter> - </None> - <None Include="..\..\GPUProc\src\cuda\gpu_wrapper.tcc"> - <Filter>src\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tBeamFormer.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tCoherentStokes.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tCorrelator.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFFT_leakage.in_.parset"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.77_Stations"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.AARTFAAC"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.parset.small-test"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.run"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tFIR_Filter.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tIntToFloat.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tKernel.in_.cu"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tKernel.parset.in"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tKernel.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tMultiDimArrayHostBuffer.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tStreamReadBuffer.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tTranspose.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\Vizualize_leakage.py"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\CMakeLists.txt"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\t_cuda_complex.cu"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\t_cuda_complex.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\CMakeLists.txt"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\Correlator.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\DelayAndBandPass.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\FFT.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\fft2.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\FIR.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\math.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\src\opencl\NewCorrelator.cl"> - <Filter>src\opencl</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tCoherentStokesTranspose.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\src\rtcp.log_prop"> - <Filter>src</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\BeamFormer.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\CoherentStokes.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\CoherentStokesTranspose.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\Correlator.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\DelayAndBandPass.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\FIR_Filter.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\gpu_math.cuh"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\IncoherentStokes.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\IncoherentStokesTranspose.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\IntToFloat.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\IntToFloat.cuh"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\Transpose.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBeamFormerKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tCoherentStokesKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tCorrelatorKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tDelayAndBandPassKernel2.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tFFT_Kernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tFIR_FilterKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tIntToFloatKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tKernelFunctions.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel.sh"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tBandPassCorrectionKernel2.in_parset"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\cuda\tTranspose2.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.parset"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tBeamFormerSubbandProcProcessSb.sh"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.parset"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tCoherentStokesBeamFormerSubbandProcProcessSb.sh"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.parset"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tCorrelatorSubbandProcProcessSb.sh"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\share\gpu\kernels\BandPassCorrection.cu"> - <Filter>share</Filter> - </None> - <None Include="..\..\GPUProc\test\tBeamform_1sec_1st_5sb_noflagging_2SapDivNTab.parset" /> - <None Include="..\..\GPUProc\test\tBeamform_1sec_1st_5sb_noflagging_2SapDivNTab.run" /> - <None Include="..\..\GPUProc\test\tBeamform_1sec_1st_5sb_noflagging_2SapDivNTab.sh" /> - <None Include="..\..\GPUProc\test\cuda\tBandPassCorrection.sh"> - <Filter>test\cuda</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\tKernelPerformance.py"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\Kernels\visualizeBeamformer.py"> - <Filter>test\Kernels</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tFlysEyeBeamFormerSubbandProcProcessSb.parset"> - <Filter>test\SubbandProcs</Filter> - </None> - <None Include="..\..\GPUProc\test\SubbandProcs\tFlysEyeBeamFormerSubbandProcProcessSb.sh"> - <Filter>test\SubbandProcs</Filter> - </None> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\GPUProc\src\CMakeLists.txt"> - <Filter>src</Filter> - </Text> - <Text Include="..\..\GPUProc\share\gpu\kernels\CMakeLists.txt"> - <Filter>share</Filter> - </Text> - <Text Include="..\..\GPUProc\test\Kernels\CMakeLists.txt"> - <Filter>test\Kernels</Filter> - </Text> - <Text Include="..\..\GPUProc\test\CMakeLists.txt"> - <Filter>test</Filter> - </Text> - <Text Include="..\..\GPUProc\test\Pipelines\CMakeLists.txt"> - <Filter>test\Pipelines</Filter> - </Text> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.user b/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/GPUProc/GPUProc.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj b/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj deleted file mode 100644 index 4dd0ad3526fb2060b3a879c99b544d566a48c2a1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj +++ /dev/null @@ -1,161 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{075D5E72-BA47-4D6E-B1E1-DBCF747CA33D}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>InputProc</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;..\..\..\static_fixture;..\..\;..\symbolic_links;C:\Program Files (x86)\boost\boost_1_41</IncludePath> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\InputProc\src\Buffer\Ranges.cc" /> - <ClCompile Include="..\..\InputProc\src\Buffer\StationID.cc" /> - <ClCompile Include="..\..\InputProc\src\Delays\Delays.cc" /> - <ClCompile Include="..\..\InputProc\src\Delays\printDelays.cc" /> - <ClCompile Include="..\..\InputProc\src\RSPBoards.cc" /> - <ClCompile Include="..\..\InputProc\src\RSPTimeStamp.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\filterRSP.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\generate.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\generateRSP.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\Generator.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\PacketFactory.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\PacketReader.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\printRSP.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\repairRSP.cc" /> - <ClCompile Include="..\..\InputProc\src\Station\RSPPacketFactory.cc" /> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIReceiveStations.cc" /> - <ClCompile Include="..\..\InputProc\src\Transpose\MPISendStation.cc" /> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIUtil.cc" /> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIUtil2.cc" /> - <ClCompile Include="..\..\InputProc\test\stationReceiver.cc" /> - <ClCompile Include="..\..\InputProc\test\stationSender.cc" /> - <ClCompile Include="..\..\InputProc\test\tDelays.cc" /> - <ClCompile Include="..\..\InputProc\test\tGenerator.cc" /> - <ClCompile Include="..\..\InputProc\test\tMPI.cc" /> - <ClCompile Include="..\..\InputProc\test\tMPIUtil2.cc" /> - <ClCompile Include="..\..\InputProc\test\tPacketFactory.cc" /> - <ClCompile Include="..\..\InputProc\test\tPacketReader.cc" /> - <ClCompile Include="..\..\InputProc\test\tRanges.cc" /> - <ClCompile Include="..\..\InputProc\test\tRSP.cc" /> - <ClCompile Include="..\..\InputProc\test\tRSPTimeStamp.cc" /> - <ClCompile Include="..\..\InputProc\test\t_generateRSP.cc" /> - </ItemGroup> - <ItemGroup> - <None Include="..\..\InputProc\src\Delays\printDelays.log_prop" /> - <None Include="..\..\InputProc\src\mpirun.sh.in" /> - <None Include="..\..\InputProc\src\Station\generateRSP.log_prop" /> - <None Include="..\..\InputProc\test\tMPI.run" /> - <None Include="..\..\InputProc\test\tMPI.sh" /> - <None Include="..\..\InputProc\test\tMPISendReceive.py" /> - <None Include="..\..\InputProc\test\tMPIUtil2.run" /> - <None Include="..\..\InputProc\test\tMPIUtil2.sh" /> - <None Include="..\..\InputProc\test\tPacketReader.in_16bit" /> - <None Include="..\..\InputProc\test\tPacketReader.in_8bit" /> - <None Include="..\..\InputProc\test\tPacketReader.sh" /> - <None Include="..\..\InputProc\test\tRSP.in_16bit" /> - <None Include="..\..\InputProc\test\tRSP.in_8bit" /> - <None Include="..\..\InputProc\test\tRSP.sh" /> - <None Include="..\..\InputProc\test\tRSP.stdout" /> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\InputProc\src\CMakeLists.txt" /> - <Text Include="..\..\InputProc\test\CMakeLists.txt" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\InputProc\src\Buffer\BoardMode.h" /> - <ClInclude Include="..\..\InputProc\src\Buffer\Ranges.h" /> - <ClInclude Include="..\..\InputProc\src\Buffer\StationID.h" /> - <ClInclude Include="..\..\InputProc\src\Delays\Delays.h" /> - <ClInclude Include="..\..\InputProc\src\obsolete\MPI_RMA.h" /> - <ClInclude Include="..\..\InputProc\src\obsolete\TimeSync.h" /> - <ClInclude Include="..\..\InputProc\src\RSPBoards.h" /> - <ClInclude Include="..\..\InputProc\src\RSPTimeStamp.h" /> - <ClInclude Include="..\..\InputProc\src\SampleType.h" /> - <ClInclude Include="..\..\InputProc\src\Station\Generator.h" /> - <ClInclude Include="..\..\InputProc\src\Station\PacketFactory.h" /> - <ClInclude Include="..\..\InputProc\src\Station\PacketReader.h" /> - <ClInclude Include="..\..\InputProc\src\Station\PacketStream.h" /> - <ClInclude Include="..\..\InputProc\src\Station\RSP.h" /> - <ClInclude Include="..\..\InputProc\src\Station\RSPPacketFactory.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MapUtil.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIProtocol.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIReceiveStations.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MPISendStation.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIUtil.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIUtil2.h" /> - <ClInclude Include="..\..\InputProc\src\Transpose\ReceiveStations.h" /> - <ClInclude Include="..\..\InputProc\src\WallClockTime.h" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.filters b/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.filters deleted file mode 100644 index 9f43d1871630331cec95de944830e8eb1f44f47c..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.filters +++ /dev/null @@ -1,247 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="src"> - <UniqueIdentifier>{c12d8bb3-acd2-4457-9a0a-6959967e3d3a}</UniqueIdentifier> - </Filter> - <Filter Include="test"> - <UniqueIdentifier>{bde46e8a-a92b-43d4-8641-dd64bb37dd31}</UniqueIdentifier> - </Filter> - <Filter Include="src\Buffer"> - <UniqueIdentifier>{52deec4f-edc9-4849-bb82-7ce18b3aeb31}</UniqueIdentifier> - </Filter> - <Filter Include="src\Delays"> - <UniqueIdentifier>{8fe086e8-829a-4107-833b-f31242df2f44}</UniqueIdentifier> - </Filter> - <Filter Include="src\obsolete"> - <UniqueIdentifier>{f4b8635d-e70e-4830-94f9-2005d091dd8d}</UniqueIdentifier> - </Filter> - <Filter Include="src\Station"> - <UniqueIdentifier>{a60f4598-e2bb-488f-b282-61711a9abf47}</UniqueIdentifier> - </Filter> - <Filter Include="src\Transpose"> - <UniqueIdentifier>{f832b4d5-4102-4daf-bdfc-869b3683a70f}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\InputProc\test\t_generateRSP.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tDelays.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tGenerator.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tMPI.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tMPIUtil2.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tPacketFactory.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tPacketReader.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tRanges.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tRSP.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\tRSPTimeStamp.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\stationReceiver.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\test\stationSender.cc"> - <Filter>test</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\RSPBoards.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\RSPTimeStamp.cc"> - <Filter>src</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Buffer\StationID.cc"> - <Filter>src\Buffer</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Buffer\Ranges.cc"> - <Filter>src\Buffer</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Delays\printDelays.cc"> - <Filter>src\Delays</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Delays\Delays.cc"> - <Filter>src\Delays</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\PacketReader.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\printRSP.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\repairRSP.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\RSPPacketFactory.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\filterRSP.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\generate.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\generateRSP.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\Generator.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Station\PacketFactory.cc"> - <Filter>src\Station</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIUtil.cc"> - <Filter>src\Transpose</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIUtil2.cc"> - <Filter>src\Transpose</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Transpose\MPIReceiveStations.cc"> - <Filter>src\Transpose</Filter> - </ClCompile> - <ClCompile Include="..\..\InputProc\src\Transpose\MPISendStation.cc"> - <Filter>src\Transpose</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="..\..\InputProc\test\tMPI.run"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tMPI.sh"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tMPIUtil2.run"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tMPIUtil2.sh"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tPacketReader.in_8bit"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tPacketReader.in_16bit"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tPacketReader.sh"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tRSP.in_8bit"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tRSP.in_16bit"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tRSP.sh"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tRSP.stdout"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\test\tMPISendReceive.py"> - <Filter>test</Filter> - </None> - <None Include="..\..\InputProc\src\mpirun.sh.in"> - <Filter>src</Filter> - </None> - <None Include="..\..\InputProc\src\Delays\printDelays.log_prop"> - <Filter>src\Delays</Filter> - </None> - <None Include="..\..\InputProc\src\Station\generateRSP.log_prop"> - <Filter>src\Station</Filter> - </None> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\InputProc\test\CMakeLists.txt"> - <Filter>test</Filter> - </Text> - <Text Include="..\..\InputProc\src\CMakeLists.txt"> - <Filter>src</Filter> - </Text> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\InputProc\src\SampleType.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\WallClockTime.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\RSPBoards.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\RSPTimeStamp.h"> - <Filter>src</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Buffer\Ranges.h"> - <Filter>src\Buffer</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Buffer\StationID.h"> - <Filter>src\Buffer</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Buffer\BoardMode.h"> - <Filter>src\Buffer</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Delays\Delays.h"> - <Filter>src\Delays</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\obsolete\TimeSync.h"> - <Filter>src\obsolete</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\obsolete\MPI_RMA.h"> - <Filter>src\obsolete</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\PacketFactory.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\PacketReader.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\PacketStream.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\RSP.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\RSPPacketFactory.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Station\Generator.h"> - <Filter>src\Station</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MPISendStation.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIUtil.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIUtil2.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\ReceiveStations.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MapUtil.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIProtocol.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - <ClInclude Include="..\..\InputProc\src\Transpose\MPIReceiveStations.h"> - <Filter>src\Transpose</Filter> - </ClInclude> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.user b/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/InputProc/InputProc.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj b/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj deleted file mode 100644 index f90dad407faa21648521ec043bd111146a36a5bf..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj +++ /dev/null @@ -1,125 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\OutputProc\src\FastFileStream.h" /> - <ClInclude Include="..\..\OutputProc\src\GPUProcIO.h" /> - <ClInclude Include="..\..\OutputProc\src\InputThread.h" /> - <ClInclude Include="..\..\OutputProc\src\IOPriority.h" /> - <ClInclude Include="..\..\OutputProc\src\MeasurementSetFormat.h" /> - <ClInclude Include="..\..\OutputProc\src\MSWriter.h" /> - <ClInclude Include="..\..\OutputProc\src\MSWriterCorrelated.h" /> - <ClInclude Include="..\..\OutputProc\src\MSWriterDAL.h" /> - <ClInclude Include="..\..\OutputProc\src\MSWriterFile.h" /> - <ClInclude Include="..\..\OutputProc\src\MSWriterNull.h" /> - <ClInclude Include="..\..\OutputProc\src\OutputThread.h" /> - <ClInclude Include="..\..\OutputProc\src\SubbandWriter.h" /> - <ClInclude Include="..\..\OutputProc\src\TBB_StaticMapping.h" /> - <ClInclude Include="..\..\OutputProc\src\TBB_Writer.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\OutputProc\src\createHeaders.cc" /> - <ClCompile Include="..\..\OutputProc\src\FastFileStream.cc" /> - <ClCompile Include="..\..\OutputProc\src\GPUProcIO.cc" /> - <ClCompile Include="..\..\OutputProc\src\InputThread.cc" /> - <ClCompile Include="..\..\OutputProc\src\MeasurementSetFormat.cc" /> - <ClCompile Include="..\..\OutputProc\src\MSWriter.cc" /> - <ClCompile Include="..\..\OutputProc\src\MSWriterCorrelated.cc" /> - <ClCompile Include="..\..\OutputProc\src\MSWriterDAL.cc" /> - <ClCompile Include="..\..\OutputProc\src\MSWriterFile.cc" /> - <ClCompile Include="..\..\OutputProc\src\MSWriterNull.cc" /> - <ClCompile Include="..\..\OutputProc\src\outputProc.cc" /> - <ClCompile Include="..\..\OutputProc\src\OutputThread.cc" /> - <ClCompile Include="..\..\OutputProc\src\plotMS.cc" /> - <ClCompile Include="..\..\OutputProc\src\SubbandWriter.cc" /> - <ClCompile Include="..\..\OutputProc\src\TBB_StaticMapping.cc" /> - <ClCompile Include="..\..\OutputProc\src\TBB_Writer.cc" /> - <ClCompile Include="..\..\OutputProc\src\TBB_Writer_main.cc" /> - <ClCompile Include="..\..\OutputProc\test\tOutputThread.cc" /> - </ItemGroup> - <ItemGroup> - <None Include="..\..\OutputProc\src\gnuplotMS.sh" /> - <None Include="..\..\OutputProc\src\outputProc.log_prop" /> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\OutputProc\src\CMakeLists.txt" /> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{AC4EED56-6048-4D80-8350-F5A753238CC3}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>OutputProc</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;..\..\..\static_fixture;..\..\;..\symbolic_links;C:\Program Files (x86)\boost\boost_1_41</IncludePath> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.filters b/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.filters deleted file mode 100644 index 68d2de9084db529e60693d804e6d25959a5e6a30..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.filters +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - <Filter Include="test"> - <UniqueIdentifier>{1f37e2bb-b5d7-4a2c-8cd6-6cf697a2ab2f}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\OutputProc\src\GPUProcIO.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\InputThread.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\IOPriority.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MeasurementSetFormat.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MSWriter.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MSWriterCorrelated.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MSWriterDAL.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MSWriterFile.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\MSWriterNull.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\OutputThread.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\SubbandWriter.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\TBB_StaticMapping.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\TBB_Writer.h"> - <Filter>Source Files</Filter> - </ClInclude> - <ClInclude Include="..\..\OutputProc\src\FastFileStream.h"> - <Filter>Source Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\OutputProc\src\InputThread.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MeasurementSetFormat.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MSWriter.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MSWriterCorrelated.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MSWriterDAL.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MSWriterFile.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\MSWriterNull.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\outputProc.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\OutputThread.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\plotMS.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\SubbandWriter.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\TBB_StaticMapping.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\TBB_Writer.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\TBB_Writer_main.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\createHeaders.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\FastFileStream.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\src\GPUProcIO.cc"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="..\..\OutputProc\test\tOutputThread.cc"> - <Filter>test</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="..\..\OutputProc\src\outputProc.log_prop"> - <Filter>Source Files</Filter> - </None> - <None Include="..\..\OutputProc\src\gnuplotMS.sh"> - <Filter>Source Files</Filter> - </None> - </ItemGroup> - <ItemGroup> - <Text Include="..\..\OutputProc\src\CMakeLists.txt"> - <Filter>Source Files</Filter> - </Text> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.user b/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/OutputProc/OutputProc.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/symbolic_links/create_links.bat b/RTCP/Cobalt/VisualStudio/symbolic_links/create_links.bat deleted file mode 100644 index 4453bc7f27a28956ecbcf65d4945ada106c80d3a..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/symbolic_links/create_links.bat +++ /dev/null @@ -1,5 +0,0 @@ -rem To add the symbolic links. Run this program from a administrator command prompt -mklink /D GPUProc ..\..\GPUProc\src -mklink /D CoInterface ..\..\CoInterface\src -mklink /D InputProc ..\..\InputProc\src -mklink /D Common ..\..\..\..\LCS\Common\include\Common \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj b/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj deleted file mode 100644 index 0d758a37693bbc6acfc65d7259cd5fc1b7d2b909..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj +++ /dev/null @@ -1,91 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{0F2BF295-EFF5-491F-8B5E-9B45C4E23C84}</ProjectGuid> - <RootNamespace>t_cuda_complex</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 5.0.props" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;..\..\..\static_fixture;..\..\;..\symbolic_links;C:\Program Files (x86)\boost\boost_1_41;C:\Program Files (x86)\AMD APP\include;..\symbolic_links\GPUProc;..\..\..\static_fixture\UnitTest++</IncludePath> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - </ClCompile> - <Link> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <AdditionalDependencies>cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PostBuildEvent> - <Command>echo copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)" -copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)"</Command> - </PostBuildEvent> - <CudaCompile> - <Defines>USE_CUDA</Defines> - </CudaCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - </ClCompile> - <Link> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <SubSystem>Console</SubSystem> - <AdditionalDependencies>cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PostBuildEvent> - <Command>echo copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)" -copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)"</Command> - </PostBuildEvent> - </ItemDefinitionGroup> - <ItemGroup> - <CudaCompile Include="..\..\GPUProc\test\complex_kernel.cu" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 5.0.targets" /> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj.user b/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/t_cuda_complex/t_cuda_complex.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/test/test.vcxproj b/RTCP/Cobalt/VisualStudio/test/test.vcxproj deleted file mode 100644 index 74e4380c241b368adb3e987442e24da5c767f6a9..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/test/test.vcxproj +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{1521604F-7418-4628-932D-40ED94A1E68E}</ProjectGuid> - <RootNamespace>test</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>MultiByte</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>MultiByte</CharacterSet> - <PlatformToolset>v120</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - </ClCompile> - <Link> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <AdditionalDependencies>cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PostBuildEvent> - <Command>echo copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)" -copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)"</Command> - </PostBuildEvent> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - </ClCompile> - <Link> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <SubSystem>Console</SubSystem> - <AdditionalDependencies>cudart.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - <PostBuildEvent> - <Command>echo copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)" -copy "$(CudaToolkitBinDir)\cudart*.dll" "$(OutDir)"</Command> - </PostBuildEvent> - </ItemDefinitionGroup> - <ItemGroup> - <CudaCompile Include="kernel.cu" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\GPUProc\test\cuda\tcreateProgram.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.cc" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/test/test.vcxproj.filters b/RTCP/Cobalt/VisualStudio/test/test.vcxproj.filters deleted file mode 100644 index 267ccf80c936bb5cb469db5c01ee68a8ea8d3297..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/test/test.vcxproj.filters +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <CudaCompile Include="kernel.cu" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\GPUProc\test\cuda\tcreateProgram.cc" /> - <ClCompile Include="..\..\GPUProc\test\cuda\tDelayAndBandPass.cc" /> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/RTCP/Cobalt/VisualStudio/test/test.vcxproj.user b/RTCP/Cobalt/VisualStudio/test/test.vcxproj.user deleted file mode 100644 index 695b5c78b91edfc29f77823eb642fc9ead8e15f1..0000000000000000000000000000000000000000 --- a/RTCP/Cobalt/VisualStudio/test/test.vcxproj.user +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> -</Project> \ No newline at end of file diff --git a/SAS/CMakeLists.txt b/SAS/CMakeLists.txt index a328b52f5d3a09f44eef38057ab9f9f7ace186bd..979f7c320d72d1058322a636ae5cf3cd199e7e0e 100644 --- a/SAS/CMakeLists.txt +++ b/SAS/CMakeLists.txt @@ -12,3 +12,4 @@ lofar_add_package(XML_generator) add_subdirectory(MoM) add_subdirectory(ResourceAssignment) +add_subdirectory(DataManagement) diff --git a/SAS/DataManagement/CMakeLists.txt b/SAS/DataManagement/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce21ce26635aefb90549929ef851bb3dd2176e4d --- /dev/null +++ b/SAS/DataManagement/CMakeLists.txt @@ -0,0 +1,5 @@ +# $Id: CMakeLists.txt 34529 2016-05-24 17:00:41Z mol $ + +lofar_add_package(DataManagementCommon) +lofar_add_package(CleanupService) +lofar_add_package(StorageQueryService) diff --git a/SAS/DataManagement/CleanupService/CMakeLists.txt b/SAS/DataManagement/CleanupService/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9d37337b1ad7df2ac979d2b40a35023ecf4436c9 --- /dev/null +++ b/SAS/DataManagement/CleanupService/CMakeLists.txt @@ -0,0 +1,24 @@ +# $Id$ + +lofar_package(CleanupService 1.0 DEPENDS PyMessaging DataManagementCommon) + +lofar_find_package(Python 2.6 REQUIRED) +include(PythonInstall) + +set(_py_files + __init__.py + config.py + rpc.py + service.py +) + +python_install(${_py_files} DESTINATION lofar/sas/datamanagement/cleanup) + +lofar_add_bin_scripts(cleanup cleanupservice) + +# supervisord config files +install(FILES + cleanupservice.ini + DESTINATION etc/supervisord.d) + +add_subdirectory(test) diff --git a/SAS/DataManagement/CleanupService/__init__.py b/SAS/DataManagement/CleanupService/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fbbab2d1199b1de168c7f25fb5e7eac727b3d066 --- /dev/null +++ b/SAS/DataManagement/CleanupService/__init__.py @@ -0,0 +1 @@ +# $Id$ diff --git a/SAS/DataManagement/CleanupService/cleanup b/SAS/DataManagement/CleanupService/cleanup new file mode 100644 index 0000000000000000000000000000000000000000..430be29b3b9d1b6af06838994cd1162f3c6f27f2 --- /dev/null +++ b/SAS/DataManagement/CleanupService/cleanup @@ -0,0 +1,10 @@ +#!/usr/bin/python +# $Id: radbclient 33373 2016-01-22 11:01:15Z schaap $ + +''' +do cleanup actions on cep4 from the commandline +''' + +if __name__ == '__main__': + from lofar.sas.datamanagement.cleanup.rpc import main + main() diff --git a/SAS/DataManagement/CleanupService/cleanupservice b/SAS/DataManagement/CleanupService/cleanupservice new file mode 100644 index 0000000000000000000000000000000000000000..82c8f4c2e437e57eae846d469735a03bd6457cb8 --- /dev/null +++ b/SAS/DataManagement/CleanupService/cleanupservice @@ -0,0 +1,10 @@ +#!/usr/bin/python +# $Id: radbservice 33373 2016-01-22 11:01:15Z schaap $ + +''' +runs the cleanup service +''' + +if __name__ == '__main__': + from lofar.sas.datamanagement.cleanup.service import main + main() diff --git a/SAS/DataManagement/CleanupService/cleanupservice.ini b/SAS/DataManagement/CleanupService/cleanupservice.ini new file mode 100644 index 0000000000000000000000000000000000000000..b92b6b0636f47388aaf7880c819a969d4f93d4c6 --- /dev/null +++ b/SAS/DataManagement/CleanupService/cleanupservice.ini @@ -0,0 +1,8 @@ +[program:cleanupservice] +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec cleanupservice' +user=lofarsys +stopsignal=INT ; KeyboardInterrupt +stopasgroup=true ; bash does not propagate signals +stdout_logfile=%(program_name)s.log +redirect_stderr=true +stderr_logfile=NONE diff --git a/SAS/DataManagement/CleanupService/config.py b/SAS/DataManagement/CleanupService/config.py new file mode 100644 index 0000000000000000000000000000000000000000..0e8dafdf6f6e028bf3a25ea448af70aa5595baa2 --- /dev/null +++ b/SAS/DataManagement/CleanupService/config.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +# $Id$ + +from lofar.messaging import adaptNameToEnvironment + +DEFAULT_BUSNAME = adaptNameToEnvironment('lofar.dm.command') +DEFAULT_SERVICENAME = 'CleanupService' diff --git a/SAS/DataManagement/CleanupService/rpc.py b/SAS/DataManagement/CleanupService/rpc.py new file mode 100644 index 0000000000000000000000000000000000000000..28817acc77830eaf03e55b0b036808b9167e6d51 --- /dev/null +++ b/SAS/DataManagement/CleanupService/rpc.py @@ -0,0 +1,66 @@ +#!/usr/bin/python + +import logging +from lofar.messaging.RPC import RPC, RPCException, RPCWrapper +from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME, DEFAULT_SERVICENAME + +logger = logging.getLogger(__name__) + +class CleanupRPC(RPCWrapper): + def __init__(self, busname=DEFAULT_BUSNAME, + servicename=DEFAULT_SERVICENAME, + broker=None): + super(CleanupRPC, self).__init__(busname, servicename, broker, timeout=18000) + + def getPathForOTDBId(self, otdb_id): + return self.rpc('GetPathForOTDBId', otdb_id=otdb_id) + + def removePath(self, path): + return self.rpc('RemovePath', path=path) + + def removeTaskData(self, otdb_id, delete_is=True, delete_cs=True, delete_uv=True, delete_im=True, delete_img=True, delete_pulp=True, delete_scratch=True): + return self.rpc('RemoveTaskData', otdb_id=otdb_id, delete_is=delete_is, delete_cs=delete_cs, delete_uv=delete_uv, delete_im=delete_im, delete_img=delete_img, delete_pulp=delete_pulp, delete_scratch=delete_scratch) + +def main(): + import sys + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser('%prog [options] <otdb_id>', + description='do cleanup actions on cep4 from the commandline') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option('-b', '--busname', dest='busname', type='string', default=DEFAULT_BUSNAME, help='Name of the bus exchange on the qpid broker, default: %s' % DEFAULT_BUSNAME) + parser.add_option('-s', '--servicename', dest='servicename', type='string', default=DEFAULT_SERVICENAME, help='Name for this service, default: %s' % DEFAULT_SERVICENAME) + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + if len(args) == 0: + parser.print_help() + exit(1) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO if options.verbose else logging.WARN) + + with CleanupRPC(busname=options.busname, servicename=options.servicename, broker=options.broker) as rpc: + otdb_id = int(args[0]) + path_result = rpc.getPathForOTDBId(otdb_id) + if path_result['found']: + path = path_result['path'] + scratch_paths = path_result['scratch_paths'] + paths = scratch_paths + [path] + print "This will delete everything in '%s'." % ', '.join(paths) + if raw_input("Are you sure? (y/n) ") == 'y': + result = rpc.removeTaskData(otdb_id) + print + if not result['deleted']: + print 'Could not delete data for task with otdb_id=%s' % otdb_id + print result['message'] + exit(0 if result['deleted'] else 1) + else: + print "Nothing deleted" + else: + print path_result['message'] + exit(1) + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/CleanupService/service.py b/SAS/DataManagement/CleanupService/service.py new file mode 100644 index 0000000000000000000000000000000000000000..d02babcfa5f2740672ff38c2b391bd5579f2574a --- /dev/null +++ b/SAS/DataManagement/CleanupService/service.py @@ -0,0 +1,276 @@ +#!/usr/bin/python +# $Id$ + +''' +''' +import logging +import os.path +import socket +import subprocess +from datetime import datetime +from optparse import OptionParser +from lofar.messaging import Service +from lofar.messaging import EventMessage, ToBus +from lofar.messaging.Service import MessageHandlerInterface +from lofar.messaging import setQpidLogLevel +from lofar.common.util import waitForInterrupt + +from lofar.sas.datamanagement.common.config import CEP4_DATA_MOUNTPOINT +from lofar.sas.datamanagement.common.path import PathResolver +from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME, DEFAULT_SERVICENAME +from lofar.sas.datamanagement.common.config import DEFAULT_DM_NOTIFICATION_BUSNAME, DEFAULT_DM_NOTIFICATION_PREFIX +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +logger = logging.getLogger(__name__) + +class CleanupHandler(MessageHandlerInterface): + def __init__(self, + mountpoint=CEP4_DATA_MOUNTPOINT, + radb_busname=RADB_BUSNAME, + radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + notification_busname=DEFAULT_DM_NOTIFICATION_BUSNAME, + notification_prefix=DEFAULT_DM_NOTIFICATION_PREFIX, + broker=None, + **kwargs): + + super(CleanupHandler, self).__init__(**kwargs) + + self.path_resolver = PathResolver(mountpoint=mountpoint, + radb_busname=radb_busname, + radb_servicename=radb_servicename, + mom_busname=mom_busname, + mom_servicename=mom_servicename, + broker=broker) + + self.notification_prefix = notification_prefix + self.event_bus = ToBus(notification_busname, broker=broker) + + self.service2MethodMap = { 'GetPathForOTDBId': self.path_resolver.getPathForOTDBId, + 'RemovePath': self._removePath, + 'RemoveTaskData': self._removeTaskData } + + def prepare_loop(self): + super(CleanupHandler, self).prepare_loop() + self.path_resolver.open() + self.event_bus.open() + logger.info("cleanup service started with projects_path=%s", self.path_resolver.projects_path) + + def finalize_loop(self): + self.path_resolver.close() + self.event_bus.close() + super(CleanupHandler, self).finalize_loop() + + def _sendPathDeletedNotification(self, path): + try: + msg = EventMessage(context=self.notification_prefix + 'PathDeleted', content={ 'path': path }) + logger.info('Sending notification with subject %s to %s: %s', msg.subject, self.event_bus.address, msg.content) + self.event_bus.send(msg) + except Exception as e: + logger.error(str(e)) + + def _sendTaskDeletedNotification(self, deleted, otdb_id, paths, message): + try: + msg = EventMessage(context=self.notification_prefix + 'TaskDeleted', content={'deleted':deleted, + 'otdb_id':otdb_id, + 'paths': paths, + 'message': message}) + logger.info('Sending notification with subject %s to %s: %s', msg.subject, self.event_bus.address, msg.content) + self.event_bus.send(msg) + except Exception as e: + logger.error(str(e)) + + def _removeTaskData(self, otdb_id, delete_is=True, delete_cs=True, delete_uv=True, delete_im=True, delete_img=True, delete_pulp=True, delete_scratch=True): + logger.info("Remove task data for otdb_id %s" % (otdb_id,)) + + if not isinstance(otdb_id, int): + message = "Provided otdb_id is not an int" + logger.error(message) + return {'deleted': False, 'message': message} + + radbrpc = self.path_resolver.radbrpc + task = radbrpc.getTask(otdb_id=otdb_id) + if task: + suc_tasks = radbrpc.getTasks(task_ids=task['successor_ids']) + unfinished_scu_tasks = [t for t in suc_tasks if not (t['status'] == 'finished' or t['status'] == 'obsolete')] + if unfinished_scu_tasks: + message = "Task otdb_id=%s has unfinished successor tasks (otdb_ids: %s)" % (task['otdb_id'], [t['otdb_id'] for t in unfinished_scu_tasks]) + logger.error(message) + return {'deleted': False, 'message': message} + + path_result = self.path_resolver.getPathForOTDBId(otdb_id) + if path_result['found']: + rm_results = [] + if delete_is and delete_cs and delete_uv and delete_im and delete_img and delete_pulp: + rm_results.append(self._removePath(path_result['path'])) + else: + if delete_is and self.path_resolver.pathExists(os.path.join(path_result['path'], 'is')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'is'))) + if delete_cs and self.path_resolver.pathExists(os.path.join(path_result['path'], 'cs')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'cs'))) + if delete_uv and self.path_resolver.pathExists(os.path.join(path_result['path'], 'uv')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'uv'))) + if delete_im and self.path_resolver.pathExists(os.path.join(path_result['path'], 'im')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'im'))) + if delete_img and self.path_resolver.pathExists(os.path.join(path_result['path'], 'img')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'img'))) + if delete_pulp and self.path_resolver.pathExists(os.path.join(path_result['path'], 'pulp')): + rm_results.append(self._removePath(os.path.join(path_result['path'], 'pulp'))) + + if delete_scratch and 'scratch_paths' in path_result: + for scratch_path in path_result['scratch_paths']: + rm_results.append(self._removePath(scratch_path)) + + rm_result = {'deleted': all(x['deleted'] for x in rm_results), + 'paths': [x.get('path') for x in rm_results], + 'message': '\n'.join(x.get('message','') for x in rm_results)} + + self._sendTaskDeletedNotification(rm_result['deleted'], otdb_id, rm_result['paths'], rm_result['message']) + + self._endStorageResourceClaim(otdb_id) + + return rm_result + + return {'deleted': False, 'message': path_result['message']} + + def _endStorageResourceClaim(self, otdb_id): + try: + #check if all data has actually been removed, + #and adjust end time of claim on storage + path_result = self.path_resolver.getPathForOTDBId(otdb_id) + if path_result['found']: + path = path_result['path'] + + if not self.path_resolver.pathExists(path): + # data was actually deleted + #update resource claim + radbrpc = self.path_resolver.radbrpc + storage_resources = radbrpc.getResources(resource_types='storage') + cep4_storage_resource = next(x for x in storage_resources if 'cep4' in x['name']) + task = radbrpc.getTask(otdb_id=otdb_id) + if task: + claims = radbrpc.getResourceClaims(task_ids=task['id'], resource_type='storage') + cep4_storage_claim_ids = [c['id'] for c in claims if c['resource_id'] == cep4_storage_resource['id']] + for claim_id in cep4_storage_claim_ids: + logger.info("setting endtime for claim %s on resource %s %s to now", claim_id, cep4_storage_resource['id'], cep4_storage_resource['name']) + radbrpc.updateResourceClaim(claim_id, endtime=datetime.utcnow()) + except Exception as e: + logger.error(str(e)) + + def _removePath(self, path): + logger.info("Remove path: %s" % (path,)) + + # do various sanity checking to prevent accidental deletes + if not isinstance(path, basestring): + message = "Provided path is not a string" + logger.error(message) + return {'deleted': False, 'message': message, 'path': path} + + if not path: + message = "Empty path provided" + logger.error(message) + return {'deleted': False, 'message': message, 'path': path} + + if '*' in path or '?' in path: + message = "Invalid path '%s': No wildcards allowed" % (path,) + logger.error(message) + return {'deleted': False, 'message': message, 'path': path} + + # remove any trailing slashes + if len(path) > 1: + path = path.rstrip('/') + + required_base_paths = [self.path_resolver.projects_path, self.path_resolver.scratch_path, self.path_resolver.share_path] + + if not any(path.startswith(base_path) for base_path in required_base_paths): + message = "Invalid path '%s': Path does not start with any oth the base paths: '%s'" % (path, ' '.join(required_base_paths)) + logger.error(message) + return {'deleted': False, 'message': message, 'path': path} + + for base_path in required_base_paths: + if path.startswith(base_path) and path[len(base_path):].count('/') == 0: + message = "Invalid path '%s': Path should be a subdir of '%s'" % (path, base_path) + logger.error(message) + return {'deleted': False, 'message': message, 'path': path} + + if not self.path_resolver.pathExists(path): + message = "Nothing to delete, path '%s' does not exist." % (path) + logger.warn(message) + return {'deleted': True, 'message': message, 'path': path} + + logger.info("Attempting to delete %s", path) + + cmd = ['rm', '-rf', path] + hostname = socket.gethostname() + if not 'mgmt0' in hostname: + cmd = ['ssh', 'lofarsys@mgmt01.cep4.control.lofar'] + cmd + logger.info(' '.join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + + if proc.returncode == 0: + message = "Deleted '%s'" % (path) + logger.info(message) + self._sendPathDeletedNotification(path) + return {'deleted': True, 'message': message, 'path': path} + + return {'deleted': False, + 'message': 'Failed to delete (part of) %s' % path, + 'path': path } + +def createService(busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, broker=None, + mountpoint=CEP4_DATA_MOUNTPOINT, verbose=False, + radb_busname=RADB_BUSNAME, radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, mom_servicename=DEFAULT_MOMQUERY_SERVICENAME): + return Service(servicename, + CleanupHandler, + busname=busname, + broker=broker, + use_service_methods=True, + numthreads=4, + verbose=verbose, + handler_args={'mountpoint': mountpoint, + 'radb_busname':RADB_BUSNAME, + 'radb_servicename':RADB_SERVICENAME, + 'mom_busname':DEFAULT_MOMQUERY_BUSNAME, + 'mom_servicename':DEFAULT_MOMQUERY_SERVICENAME, + 'broker':broker}) + +def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + + # Check the invocation arguments + parser = OptionParser("%prog [options]", + description='runs the cleanup service') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option("-b", "--busname", dest="busname", type="string", default=DEFAULT_BUSNAME, help="Name of the bus exchange on the qpid broker, default: %default") + parser.add_option("-s", "--servicename", dest="servicename", type="string", default=DEFAULT_SERVICENAME, help="Name for this service, default: default %default") + parser.add_option("--mountpoint", dest="mountpoint", type="string", default=CEP4_DATA_MOUNTPOINT, help="path of local cep4 mount point, default: %default") + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + parser.add_option("--radb_busname", dest="radb_busname", type="string", default=RADB_BUSNAME, help="Name of the bus on which the RADB service listens, default: %default") + parser.add_option("--radb_servicename", dest="radb_servicename", type="string", default=RADB_SERVICENAME, help="Name of the RADB service, default: %default") + parser.add_option("--mom_busname", dest="mom_busname", type="string", default=DEFAULT_MOMQUERY_BUSNAME, help="Name of the bus on which the MoM service listens, default: %default") + parser.add_option("--mom_servicename", dest="mom_servicename", type="string", default=DEFAULT_MOMQUERY_SERVICENAME, help="Name of the MoM service, default: %default") + (options, args) = parser.parse_args() + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.DEBUG if options.verbose else logging.INFO) + setQpidLogLevel(logging.INFO) + + with createService(busname=options.busname, + servicename=options.servicename, + broker=options.broker, + verbose=options.verbose, + radb_busname=options.radb_busname, + radb_servicename=options.radb_servicename, + mom_busname=options.mom_busname, + mom_servicename=options.mom_busname): + waitForInterrupt() + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/CleanupService/test/CMakeLists.txt b/SAS/DataManagement/CleanupService/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..70fad49899886146924be3dc84ad2081fb47209b --- /dev/null +++ b/SAS/DataManagement/CleanupService/test/CMakeLists.txt @@ -0,0 +1,5 @@ +# $Id$ +include(LofarCTest) + +lofar_add_test(test_cleanup_service_and_rpc) + diff --git a/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.py b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.py new file mode 100755 index 0000000000000000000000000000000000000000..9d64e24596f927541d45014c7106559537d36e45 --- /dev/null +++ b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +import unittest +import uuid +import datetime +import logging +from lofar.messaging import Service +from qpid.messaging.exceptions import * + +try: + from qpid.messaging import Connection + from qpidtoollibs import BrokerAgent +except ImportError: + print 'Cannot run test without qpid tools' + print 'Please source qpid profile' + exit(3) + +try: + from mock import MagicMock + from mock import patch +except ImportError: + print 'Cannot run test without python MagicMock' + print 'Please install MagicMock: pip install mock' + exit(3) + +connection = None +broker = None + +try: + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + logger = logging.getLogger(__name__) + + # setup broker connection + connection = Connection.establish('127.0.0.1') + broker = BrokerAgent(connection) + + # add test service busname + busname = 'test-lofarbus-%s' % (uuid.uuid1()) + broker.addExchange('topic', busname) + + # TODO: the cleanup service does not use shutil.rmtree under the hood anymore, + # so we cannot mock that + # and we do not want to delete actual data + # so I disabled this test for now. + + ## the cleanup service uses shutil.rmtree under the hood + ## so, mock/patch shutil.rmtree and fake the delete action + ## because we do not want to delete any real data in this test + #with patch('shutil.rmtree', autospec=True) as patch_rmtree: + #mock_rmtree = patch_rmtree.return_value + #mock_rmtree.return_value = True + + #with patch('lofar.sas.resourceassignment.resourceassignmentservice.rpc.RARPC', autospec=True) as patch_rarpc: + #mock_rarpc = patch_rarpc.return_value + #mock_rarpc.getTask.side_effect = lambda otdb_id: {'id': 42, 'mom_id': 1000042} if otdb_id == 13 else {'id': 43, 'mom_id': 1000043} if otdb_id == 14 else None + + #with patch('lofar.mom.momqueryservice.momqueryrpc.MoMQueryRPC', autospec=True) as patch_momrpc: + #mock_momrpc = patch_momrpc.return_value + #mock_momrpc.getProjectDetails.return_value = {'1000042': {'project_name': 'my_project'}} + + ## now that we have a mocked the external dependencies, import cleanupservice + #from lofar.sas.datamanagement.cleanup.service import createService + #from lofar.sas.datamanagement.cleanup.rpc import CleanupRPC + + #class TestCleanupServiceAndRPC(unittest.TestCase): + #def testRemovePath(self): + #with CleanupRPC(busname=busname) as rpc: + ##try some invalid input + #self.assertFalse(rpc.removePath(None)['deleted']) + #self.assertFalse(rpc.removePath(True)['deleted']) + #self.assertFalse(rpc.removePath({'foo':'bar'})['deleted']) + #self.assertFalse(rpc.removePath(['foo', 'bar'])['deleted']) + + ##try some dangerous paths + ##these should not be deleted + #result = rpc.removePath('/') + #self.assertFalse(result['deleted']) + #self.assertTrue('Path does not start with' in result['message']) + + #result = rpc.removePath('/foo/*/bar') + #self.assertFalse(result['deleted']) + #self.assertTrue('No wildcards allowed' in result['message']) + + #result = rpc.removePath('/foo/ba?r') + #self.assertFalse(result['deleted']) + #self.assertTrue('No wildcards allowed' in result['message']) + + #result = rpc.removePath('/data') + #self.assertFalse(result['deleted']) + #self.assertTrue('Path does not start with' in result['message']) + + #result = rpc.removePath('/data/test-projects/') + #self.assertFalse(result['deleted']) + #self.assertTrue('Path should be a subdir of' in result['message']) + + #result = rpc.removePath('/data/test-projects/foo') + #self.assertFalse(result['deleted']) + #self.assertTrue('Path should be a subdir of' in result['message']) + + #result = rpc.removePath('/data/test-projects/foo/') + #self.assertFalse(result['deleted']) + #self.assertTrue('Path should be a subdir of' in result['message']) + + ##try an actual delete, should work with mocked shutil.rmtree + #self.assertTrue(rpc.removePath('/data/test-projects/foo/bar')['deleted']) + + #def testRemoveTaskData(self): + #with CleanupRPC(busname=busname) as rpc: + ##try existing otdb_id=13 + #self.assertTrue(rpc.removeTaskData(13)['deleted']) + + ##try non_existing mom_project for otdb_id=14 + #result = rpc.removeTaskData(14) + #self.assertFalse(result['deleted']) + #self.assertTrue('Could not find mom project details' in result['message']) + + ##try non_existing otdb_id=15 + #result = rpc.removeTaskData(15) + #self.assertFalse(result['deleted']) + #self.assertTrue('Could not find task' in result['message']) + + ## create and run the service + #with createService(busname=busname): + ## and run all tests + #unittest.main() + +except ConnectError as ce: + logger.error(ce) + exit(3) +finally: + # cleanup test bus and exit + if broker: + broker.delExchange(busname) + if connection: + connection.close() diff --git a/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.run b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.run new file mode 100755 index 0000000000000000000000000000000000000000..0f7141c5fce6f63f474e3b244a92f28951a78354 --- /dev/null +++ b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.run @@ -0,0 +1,6 @@ +#!/bin/bash + +# Run the unit test +source python-coverage.sh +python_coverage_test "cleanup*" test_cleanup_service_and_rpc.py + diff --git a/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.sh b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.sh new file mode 100755 index 0000000000000000000000000000000000000000..8994e8ed77ab2a9ecae740a332fdd6b1026323e4 --- /dev/null +++ b/SAS/DataManagement/CleanupService/test/test_cleanup_service_and_rpc.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./runctest.sh test_cleanup_service_and_rpc diff --git a/SAS/DataManagement/DataManagementCommon/CMakeLists.txt b/SAS/DataManagement/DataManagementCommon/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fbe4a2ccc0cd20682785ace4b5446ea09f4074c0 --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/CMakeLists.txt @@ -0,0 +1,17 @@ +# $Id$ + +lofar_package(DataManagementCommon 1.0 DEPENDS PyMessaging ResourceAssignmentService MoMQueryService) + +lofar_find_package(Python 2.6 REQUIRED) +include(PythonInstall) + +set(_py_files + __init__.py + config.py + path.py + datamanagementbuslistener.py +) + +lofar_add_bin_scripts(getPathForTask) + +python_install(${_py_files} DESTINATION lofar/sas/datamanagement/common) diff --git a/SAS/DataManagement/DataManagementCommon/__init__.py b/SAS/DataManagement/DataManagementCommon/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fbbab2d1199b1de168c7f25fb5e7eac727b3d066 --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/__init__.py @@ -0,0 +1 @@ +# $Id$ diff --git a/SAS/DataManagement/DataManagementCommon/config.py b/SAS/DataManagement/DataManagementCommon/config.py new file mode 100644 index 0000000000000000000000000000000000000000..f008c9f12ff4f7358914336d465a9f89eb1317cb --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/config.py @@ -0,0 +1,10 @@ +#!/usr/bin/python +# $Id$ + +from lofar.messaging import adaptNameToEnvironment + +CEP4_DATA_MOUNTPOINT='/data' + +DEFAULT_DM_NOTIFICATION_BUSNAME = adaptNameToEnvironment('lofar.dm.notification') +DEFAULT_DM_NOTIFICATION_PREFIX = 'DM.' +DEFAULT_DM_NOTIFICATION_SUBJECTS=DEFAULT_DM_NOTIFICATION_PREFIX+'*' diff --git a/SAS/DataManagement/DataManagementCommon/datamanagementbuslistener.py b/SAS/DataManagement/DataManagementCommon/datamanagementbuslistener.py new file mode 100644 index 0000000000000000000000000000000000000000..c78c5cba3f76029153030df30c4dcc6c89adfef0 --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/datamanagementbuslistener.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +# DataManagementBusListener.py +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# + +from lofar.messaging.messagebus import AbstractBusListener +from lofar.sas.datamanagement.common.config import DEFAULT_DM_NOTIFICATION_BUSNAME, DEFAULT_DM_NOTIFICATION_SUBJECTS +from lofar.common.util import waitForInterrupt + +import qpid.messaging +import logging + +logger = logging.getLogger(__name__) + +class DataManagementBusListener(AbstractBusListener): + def __init__(self, busname=DEFAULT_DM_NOTIFICATION_BUSNAME, subjects=DEFAULT_DM_NOTIFICATION_SUBJECTS, broker=None, **kwargs): + self.subject_prefix = (subjects.split('.')[0]+'.') if '.' in subjects else '' + + address = "%s/%s" % (busname, subjects) + super(DataManagementBusListener, self).__init__(address, broker, **kwargs) + + def _handleMessage(self, msg): + logger.info("on%s: %s" % (msg.subject.replace(self.subject_prefix, ''), str(msg.content).replace('\n', ' '))) + + if msg.subject == '%sTaskDeleted' % self.subject_prefix: + self.onTaskDeleted(msg.content.get('otdb_id'), msg.content.get('paths')) + elif msg.subject == '%sPathDeleted' % self.subject_prefix: + self.onPathDeleted(msg.content.get('path')) + elif msg.subject == '%sDiskUsageChanged' % self.subject_prefix: + self.onDiskUsageChanged(msg.content.get('path'), msg.content.get('disk_usage'), msg.content.get('otdb_id')) + else: + logger.error("DataManagementBusListener.handleMessage: unknown subject: %s" %str(msg.subject)) + + def onTaskDeleted(self, otdb_id, paths): + '''onTaskDeleted is called upon receiving a TaskDeleted message. + :param otdb_id: otdb_id of the deleted task + :param paths: list of paths of the deleted task''' + pass + + def onPathDeleted(self, path): + '''onPathDeleted is called upon receiving a PathDeleted message. + :param path: path of the deleted task''' + pass + + def onDiskUsageChanged(self, path, disk_usage, otdb_id=None): + '''onDiskUsageChanged is called upon receiving a DiskUsageChanged message. + :param path: path for which the disk_usage changed + :param path: the disk_usage of the path''' + pass + + + +if __name__ == '__main__': + with DataManagementBusListener(broker=None) as listener: + waitForInterrupt() + +__all__ = ["DataManagementBusListener"] diff --git a/SAS/DataManagement/DataManagementCommon/getPathForTask b/SAS/DataManagement/DataManagementCommon/getPathForTask new file mode 100644 index 0000000000000000000000000000000000000000..d0ca0d5a0195039517b308aab9823873d974b787 --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/getPathForTask @@ -0,0 +1,5 @@ +#!/usr/bin/python + +if __name__ == '__main__': + from lofar.sas.datamanagement.common.path import main + main() diff --git a/SAS/DataManagement/DataManagementCommon/path.py b/SAS/DataManagement/DataManagementCommon/path.py new file mode 100644 index 0000000000000000000000000000000000000000..489603431b9de1d37617ac2c51f98f6367b4431f --- /dev/null +++ b/SAS/DataManagement/DataManagementCommon/path.py @@ -0,0 +1,259 @@ +#!/usr/bin/python + +import os +import os.path +import logging +import socket +import subprocess + +from lofar.common import isProductionEnvironment, isTestEnvironment + +from lofar.sas.datamanagement.common.config import CEP4_DATA_MOUNTPOINT + +from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC as RADBRPC +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME + +from lofar.mom.momqueryservice.momqueryrpc import MoMQueryRPC +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +logger = logging.getLogger(__name__) + +class PathResolver: + def __init__(self, + mountpoint=CEP4_DATA_MOUNTPOINT, + radb_busname=RADB_BUSNAME, + radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + broker=None): + + self.mountpoint = mountpoint + self.projects_path = os.path.join(self.mountpoint, 'projects' if isProductionEnvironment() else 'test-projects') + self.scratch_path = os.path.join(self.mountpoint, 'scratch', 'pipeline') + self.share_path = os.path.join(self.mountpoint, 'share', 'pipeline') + + self.radbrpc = RADBRPC(busname=radb_busname, servicename=radb_servicename, broker=broker) + self.momrpc = MoMQueryRPC(busname=mom_busname, servicename=mom_servicename, broker=broker) + + def open(self): + self.radbrpc.open() + self.momrpc.open() + + def close(self): + self.radbrpc.close() + self.momrpc.close() + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def getPathForRADBId(self, radb_id): + logger.debug("Get path for radb_id %s" % (radb_id,)) + return self.getPathForTask(radb_id=radb_id) + + def getPathForMoMId(self, mom_id): + logger.debug("Get path for mom_id %s" % (mom_id,)) + return self.getPathForTask(mom_id=mom_id) + + def getPathForOTDBId(self, otdb_id): + logger.debug("Get path for otdb_id %s" % (otdb_id,)) + return self.getPathForTask(otdb_id=otdb_id) + + def getPathForTask(self, radb_id=None, mom_id=None, otdb_id=None, include_scratch_paths=True): + logger.info("getPathForTask(radb_id=%s, mom_id=%s, otdb_id=%s)", radb_id, mom_id, otdb_id) + '''get the path for a task for either the given radb_id, or for the given mom_id, or for the given otdb_id''' + result = self._getProjectPathAndDetails(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + if result['found']: + project_path = result['path'] + task = result['task'] + task_data_path = os.path.join(project_path, 'L%s' % task['otdb_id']) + logger.info("found path '%s' for otdb_id=%s mom_id=%s radb_id=%s" % (task_data_path, task['otdb_id'], task['mom_id'], task['id'])) + + path_result = {'found': True, 'message': '', 'path': task_data_path} + + if include_scratch_paths and task['type'] == 'pipeline': + path_result['scratch_paths'] = [] + + scratch_path = os.path.join(self.scratch_path, 'Observation%s' % task['otdb_id']) + share_path = os.path.join(self.share_path, 'Observation%s' % task['otdb_id']) + logger.info("Checking scratch paths %s %s for otdb_id=%s mom_id=%s radb_id=%s" % (scratch_path, share_path, task['otdb_id'], task['mom_id'], task['id'])) + + if self.pathExists(scratch_path): + path_result['scratch_paths'].append(scratch_path) + + if self.pathExists(share_path): + path_result['scratch_paths'].append(share_path) + + logger.info("result for getPathForTask(radb_id=%s, mom_id=%s, otdb_id=%s): %s", radb_id, mom_id, otdb_id, path_result) + return path_result + + logger.warn("result for getPathForTask(radb_id=%s, mom_id=%s, otdb_id=%s): %s", radb_id, mom_id, otdb_id, result) + return result + + def _getProjectPathAndDetails(self, radb_id=None, mom_id=None, otdb_id=None): + '''get the project path and details of a task for either the given radb_id, or for the given mom_id, or for the given otdb_id''' + ids = [radb_id, mom_id, otdb_id] + validIds = [x for x in ids if x != None and isinstance(x, int)] + + if len(validIds) != 1: + raise KeyError("Provide one and only one id: radb_id=%s, mom_id=%s, otdb_id=%s" % (radb_id, mom_id, otdb_id)) + + task = self.radbrpc.getTask(id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + + if not task: + message = "Could not find task in RADB for radb_id=%s, mom_id=%s, otdb_id=%s" % (radb_id, mom_id, otdb_id) + logger.error(message) + return {'found': False, 'message': message, 'path': None} + + logger.info("found radb task with radb_id=%s mom_id=%s and otdb_id=%s" % (task['id'], task['mom_id'], task['otdb_id'])) + + mom_details = self.momrpc.getProjectDetails(task['mom_id']) + + if not mom_details or str(task['mom_id']) not in mom_details: + message = "Could not find mom project details for otdb_id=%s mom_id=%s radb_id=%s" % (task['otdb_id'], task['mom_id'], task['id']) + logger.error(message) + return {'found': False, 'message': message, 'path': None} + + project_name = mom_details[str(task['mom_id'])]['project_name'] + logger.info("found project '%s' for otdb_id=%s mom_id=%s radb_id=%s" % (project_name, task['otdb_id'], task['mom_id'], task['id'])) + + project_path = os.path.join(self.projects_path, "_".join(project_name.split())) + return {'found': True, 'path': project_path, 'mom_details':mom_details, 'task':task} + + def getProjectPath(self, radb_id=None, mom_id=None, otdb_id=None): + result = self._getProjectPathAndDetails(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + + if result['found']: + del result['mom_details'] + del result['task'] + + return result + + def getProjectDirAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, project_name=None): + '''get the project directory and its subdirectories of either the project_name, or the task's project for either the given radb_id, or for the given mom_id, or for the given otdb_id''' + if project_name: + project_path = os.path.join(self.projects_path, "_".join(project_name.split())) + return self.getSubDirectories(project_path) + + result = self.getProjectPath(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + if result['found']: + return self.getSubDirectories(result['path']) + return result + + def getSubDirectoriesForOTDBId(self, otdb_id): + return self.getSubDirectoriesForTask(otdb_id=otdb_id) + + def getSubDirectoriesForMoMId(self, mom_id): + return self.getSubDirectoriesForTask(mom_id=mom_id) + + def getSubDirectoriesForRADBId(self, radb_id): + return self.getSubDirectoriesForTask(radb_id=radb_id) + + def getSubDirectoriesForTask(self, radb_id=None, mom_id=None, otdb_id=None): + result = self.getPathForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + if result['found']: + return self.getSubDirectories(result['path']) + return result + + def getSubDirectories(self, path): + logger.debug('getSubDirectories(%s)', path) + # get the subdirectories of the given path + cmd = ['lfs', 'ls', '-l', path] + hostname = socket.gethostname() + if not 'mgmt0' in hostname: + cmd = ['ssh', 'lofarsys@mgmt01.cep4.control.lofar'] + cmd + logger.debug(' '.join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + + if proc.returncode != 0: + # lfs puts it's error message in stdout + logger.error(out + err) + return {'found': False, 'path': path, 'message': out + err} + + # parse out + lines = [l.strip() for l in out.split('\n')] + dir_lines = [l for l in lines if l.startswith('drwx')] + dir_names = [l.split(' ')[-1].strip() for l in dir_lines] + + result = {'found': True, 'path': path, 'sub_directories': dir_names} + logger.debug('getSubDirectories(%s) result: %s', path, result) + return result + + def pathExists(self, path): + cmd = ['lfs', 'ls', path] + hostname = socket.gethostname() + if not 'mgmt0' in hostname: + cmd = ['ssh', 'lofarsys@mgmt01.cep4.control.lofar'] + cmd + logger.debug(' '.join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + + if proc.returncode != 0 and 'No such file or directory' in err: + return False + + return True + +def main(): + import sys + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser('%prog [options]', + description='get path for otdb_id/mom_id/radb_id') + parser.add_option('-p', '--path', dest='path', action='store_true', help='get the path for the given otdb_id/mom_id/radb_id') + parser.add_option('-P', '--project', dest='project', action='store_true', help='get the project path and all its sub directories for the given otdb_id/mom_id/radb_id') + parser.add_option('-s', '--subdirs', dest='subdirs', action='store_true', help='get the sub directories of the path for the given otdb_id/mom_id/radb_id') + parser.add_option('-o', '--otdb_id', dest='otdb_id', type='int', default=None, help='otdb_id of task to get the path for') + parser.add_option('-m', '--mom_id', dest='mom_id', type='int', default=None, help='mom_id of task to get the path for') + parser.add_option('-r', '--radb_id', dest='radb_id', type='int', default=None, help='radb_id of task to get the path for') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option("--mountpoint", dest="mountpoint", type="string", default=CEP4_DATA_MOUNTPOINT, help="path of local cep4 mount point, default: %default") + parser.add_option("--radb_busname", dest="radb_busname", type="string", default=RADB_BUSNAME, help="Name of the bus on which the RADB service listens, default: %default") + parser.add_option("--radb_servicename", dest="radb_servicename", type="string", default=RADB_SERVICENAME, help="Name of the RADB service, default: %default") + parser.add_option("--mom_busname", dest="mom_busname", type="string", default=DEFAULT_MOMQUERY_BUSNAME, help="Name of the bus on which the MoM service listens, default: %default") + parser.add_option("--mom_servicename", dest="mom_servicename", type="string", default=DEFAULT_MOMQUERY_SERVICENAME, help="Name of the MoM service, default: %default") + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + if not (options.otdb_id or options.mom_id or options.radb_id): + parser.print_help() + exit(1) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO if options.verbose else logging.WARN) + + with PathResolver(radb_busname=options.radb_busname, radb_servicename=options.radb_servicename, mom_busname=options.mom_busname, mom_servicename=options.mom_servicename, broker=options.broker) as path_resolver: + + if options.path: + result = path_resolver.getPathForTask(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id) + if result['found']: + print "path: %s" % (result['path']) + else: + print result['message'] + exit(1) + + if options.project: + result = path_resolver.getProjectDirAndSubDirectories(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id) + if result['found']: + print "projectpath: %s" % (result['path']) + print "subdirectories: %s" % (' '.join(result['sub_directories'])) + else: + print result['message'] + exit(1) + + if options.subdirs: + result = path_resolver.getSubDirectoriesForTask(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id) + if result['found']: + print "path: %s" % (result['path']) + print "subdirectories: %s" % (' '.join(result['sub_directories'])) + else: + print result['message'] + exit(1) + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/StorageQueryService/CMakeLists.txt b/SAS/DataManagement/StorageQueryService/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2db5dcd4e792192577d07eb2b4e870cceb329cc6 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/CMakeLists.txt @@ -0,0 +1,26 @@ +# $Id$ + +lofar_package(StorageQueryService 1.0 DEPENDS PyMessaging MoMQueryService DataManagementCommon OTDB_Services) + +lofar_find_package(Python 2.6 REQUIRED) +include(PythonInstall) + +set(_py_files + __init__.py + config.py + rpc.py + service.py + diskusage.py + cache.py +) + +python_install(${_py_files} DESTINATION lofar/sas/datamanagement/storagequery) + +lofar_add_bin_scripts(storagequery storagequeryservice) + +# supervisord config files +install(FILES + storagequeryservice.ini + DESTINATION etc/supervisord.d) + +add_subdirectory(test) diff --git a/SAS/DataManagement/StorageQueryService/__init__.py b/SAS/DataManagement/StorageQueryService/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fbbab2d1199b1de168c7f25fb5e7eac727b3d066 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/__init__.py @@ -0,0 +1 @@ +# $Id$ diff --git a/SAS/DataManagement/StorageQueryService/cache.py b/SAS/DataManagement/StorageQueryService/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..6416ce67631280498b71dcaeb7e7cbe4c9f9766d --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/cache.py @@ -0,0 +1,494 @@ +#!/usr/bin/python +# $Id$ + +''' +TODO: add doc +''' +import logging +import datetime +from time import sleep +import ast +from optparse import OptionParser +from threading import Thread, RLock +import os.path + +from lofar.messaging import EventMessage, ToBus +from lofar.common.util import humanreadablesize +from lofar.sas.datamanagement.storagequery.diskusage import getDiskUsageForPath as du_getDiskUsageForPath +from lofar.sas.datamanagement.storagequery.diskusage import DiskUsage +from lofar.sas.datamanagement.common.datamanagementbuslistener import DataManagementBusListener +from lofar.sas.otdb.OTDBBusListener import OTDBBusListener +from lofar.common.util import waitForInterrupt +from lofar.sas.datamanagement.common.config import CEP4_DATA_MOUNTPOINT +from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_BUSNAME, DEFAULT_OTDB_NOTIFICATION_SUBJECT +from lofar.sas.datamanagement.common.config import DEFAULT_DM_NOTIFICATION_BUSNAME, DEFAULT_DM_NOTIFICATION_PREFIX +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +logger = logging.getLogger(__name__) + +MAX_CACHE_ENTRY_AGE = datetime.timedelta(minutes=60) + +class CacheManager: + def __init__(self, + mountpoint=CEP4_DATA_MOUNTPOINT, + radb_busname=RADB_BUSNAME, + radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + otdb_notification_busname=DEFAULT_OTDB_NOTIFICATION_BUSNAME, + otdb_notification_subject=DEFAULT_OTDB_NOTIFICATION_SUBJECT, + dm_notification_busname=DEFAULT_DM_NOTIFICATION_BUSNAME, + dm_notification_prefix=DEFAULT_DM_NOTIFICATION_PREFIX, + broker=None): + self.otdb_listener = OTDBBusListener(busname=otdb_notification_busname, + subject=otdb_notification_subject, + broker=broker, + numthreads=2) + + self.otdb_listener.onObservationAborted = self.onObservationAborted + self.otdb_listener.onObservationCompleting = self.onObservationCompleting + + self.dm_listener = DataManagementBusListener(busname=dm_notification_busname, + subjects=dm_notification_prefix + '*', + broker=broker, + numthreads=2) + + self.dm_listener.onTaskDeleted = self.onTaskDeleted + + self.notification_prefix = dm_notification_prefix + self.event_bus = ToBus(dm_notification_busname, broker=broker) + + self._updateCacheThread = None + self._updateCacheThreadRunning = False + self._continueUpdateCacheThread = False + self._cacheLock = RLock() + + self._cache = {'path_du_results': {}, 'otdb_id2path': {} } + self._readCacheFromDisk() + + self.disk_usage = DiskUsage(mountpoint=mountpoint, + radb_busname=radb_busname, + radb_servicename=radb_servicename, + mom_busname=mom_busname, + mom_servicename=mom_servicename, + broker=broker) + + def _sendDiskUsageChangedNotification(self, path, disk_usage, otdb_id=None): + try: + msg = EventMessage(context=self.notification_prefix + 'DiskUsageChanged', + content={ 'path': path, + 'disk_usage': disk_usage, + 'disk_usage_readable': humanreadablesize(disk_usage), + 'otdb_id': otdb_id }) + logger.info('Sending notification with subject %s to %s: %s', msg.subject, self.event_bus.address, msg.content) + self.event_bus.send(msg) + except Exception as e: + logger.error(str(e)) + + def _readCacheFromDisk(self): + # maybe this cache on disk is slow, if so, revert to proper db solution + try: + if os.path.exists('.du_cache.py'): + with open('.du_cache.py', 'r') as file: + with self._cacheLock: + self._cache = eval(file.read().strip()) + if not isinstance(self._cache, dict): + self._cache = {'path_du_results': {}, 'otdb_id2path': {} } + except Exception as e: + logger.error("Error while reading in du cache: %s", e) + with self._cacheLock: + self._cache = {'path_du_results': {}, 'otdb_id2path': {} } + + + def _writeCacheToDisk(self): + try: + with open('.du_cache.py', 'w') as file: + with self._cacheLock: + file.write(str(self._cache)) + except Exception as e: + logger.error("Error while writing du cache: %s", e) + + def _updateCache(self, du_result): + if not 'path' in du_result: + return + + otdb_id = du_result.get('otdb_id') + + with self._cacheLock: + path = du_result['path'] + path_cache = self._cache['path_du_results'] + otdb_id2path_cache = self._cache['otdb_id2path'] + + if du_result['found']: + if not path in path_cache or path_cache[path]['disk_usage'] != du_result['disk_usage']: + logger.info('updating cache entry: %s', du_result) + path_cache[path] = du_result + self._sendDiskUsageChangedNotification(path, du_result['disk_usage'], otdb_id) + + path_cache[path]['cache_timestamp'] = datetime.datetime.utcnow() + + if otdb_id != None: + otdb_id2path_cache[otdb_id] = path + else: + if 'message' not in du_result or 'could not resolve hostname' not in du_result['message'].lower() : + if path in path_cache: + logger.info('removing path \'%s\' from cache', path) + del path_cache[path] + self._sendDiskUsageChangedNotification(path, 0, otdb_id) + + if otdb_id != None and otdb_id in otdb_id2path_cache: + otdb_id2path_cache[otdb_id] = None + + self._writeCacheToDisk() + + if du_result.get('path') == self.disk_usage.path_resolver.projects_path: + self._updateProjectsDiskUsageInRADB() + + def _invalidateCacheEntryForPath(self, path): + with self._cacheLock: + path_cache = self._cache['path_du_results'] + if path in path_cache: + path_cache[path]['cache_timestamp'] = path_cache[path]['cache_timestamp'] - MAX_CACHE_ENTRY_AGE + + def _scanProjectsTree(self): + try: + def addSubDirectoriesToCache(directory): + depth = len(directory.replace(self.disk_usage.path_resolver.projects_path, '').strip('/').split('/')) + if depth > 3: + return + + with self._cacheLock: + path_cache = self._cache['path_du_results'] + if not directory in path_cache: + logger.info('tree scan: adding \'%s\' with empty disk_usage to cache which will be du\'ed later', directory) + empty_du_result = {'found': True, 'disk_usage': None, 'path': directory, 'name': directory.split('/')[-1]} + self._updateCache(empty_du_result) + + if directory in path_cache: + # make cache entry for directory 'old', so it will be du'ed in _updateOldEntriesInCache immediately + path_cache[directory]['cache_timestamp'] -= MAX_CACHE_ENTRY_AGE + + if not self._updateCacheThreadRunning: + return + + if depth < 3: + logger.info('tree scan: scanning \'%s\'', directory) + sd_result = self.disk_usage.path_resolver.getSubDirectories(directory) + + if sd_result['found']: + subdir_paths = [os.path.join(directory,sd) for sd in sd_result['sub_directories']] + + for subdir_path in subdir_paths: + # recurse + addSubDirectoriesToCache(subdir_path) + + addSubDirectoriesToCache(self.disk_usage.path_resolver.projects_path) + logger.info('tree scan complete') + + except Exception as e: + logger.error(str(e)) + + def _updateOldEntriesInCache(self): + logger.info('starting updating old cache entries') + while self._updateCacheThreadRunning: + try: + now = datetime.datetime.utcnow() + with self._cacheLock: + path_cache = self._cache['path_du_results'] + old_entries = [cache_entry for cache_entry in path_cache.values() if now - cache_entry['cache_timestamp'] > MAX_CACHE_ENTRY_AGE] + + if old_entries: + logger.info('%s old cache entries need to be updated', len(old_entries)) + + # sort them oldest to newest, depth==2 paths first + def compareFunc(entry1, entry2): + path1 = entry1['path'] + path2 = entry2['path'] + depth1 = len(path1.replace(self.disk_usage.path_resolver.projects_path, '').strip('/').split('/')) + depth2 = len(path2.replace(self.disk_usage.path_resolver.projects_path, '').strip('/').split('/')) + + if depth1 == 2 or depth2 == 2: + if depth1 == 2 and depth2 == 2: + if not entry1['disk_usage']: + return -1 + if not entry2['disk_usage']: + return 1 + if entry1['cache_timestamp'] < entry2['cache_timestamp']: + return -1 + if entry1['cache_timestamp'] > entry2['cache_timestamp']: + return 1 + return 0 + elif depth1 == 2: + return -1 + elif depth2 == 2: + return 1 + elif depth1 < 2: + return 1 + elif depth2 < 2: + return -1 + + if entry1['cache_timestamp'] < entry2['cache_timestamp']: + return -1 + if entry1['cache_timestamp'] > entry2['cache_timestamp']: + return 1 + return 0 + + old_entries = sorted(old_entries, cmp=compareFunc) + + cacheUpdateStart = datetime.datetime.utcnow() + + for i, cache_entry in enumerate(old_entries): + try: + path = cache_entry.get('path') + if path: + logger.info('examining old entry %s/%s in cache: %s', i, len(old_entries), path) + result = du_getDiskUsageForPath(path) + if result['found']: + logger.debug('trying to update old entry in cache: %s', result) + self._updateCache(result) + except Exception as e: + logger.error(str(e)) + + if not self._updateCacheThreadRunning: + return + + if datetime.datetime.utcnow() - cacheUpdateStart > datetime.timedelta(minutes=5): + # break out of cache update loop if full update takes more than 5min + # next loop we'll start with the oldest cache entries again + logger.info('skipping remaining %s old cache entries updates, they will be updated next time', len(old_entries)-i) + break + + for i in range(60): + sleep(1) + if not self._updateCacheThreadRunning: + return + if self._continueUpdateCacheThread: + # break out of sleep loop and continue updating old cache entries + self._continueUpdateCacheThread = False + break + except Exception as e: + logger.error(str(e)) + + def _scanProjectsTreeAndUpdateOldEntriesInCache(self): + self._scanProjectsTree() + self._updateOldEntriesInCache() + + def _updateProjectsDiskUsageInRADB(self): + try: + projects_du_result = self.getDiskUsageForPath(self.disk_usage.path_resolver.projects_path) + if projects_du_result['found']: + #get the total used space, and update the resource availability in the radb + radbrpc = self.disk_usage.path_resolver.radbrpc + storage_resources = radbrpc.getResources(resource_types='storage', include_availability=True) + cep4_storage_resource = next(x for x in storage_resources if 'cep4' in x['name']) + + total_capacity = cep4_storage_resource.get('total_capacity') + used_capacity = projects_du_result.get('disk_usage') + if total_capacity != None and used_capacity != None: + available_capacity = total_capacity - used_capacity + + logger.info('updating availability capacity for %s (id=%s) to %s in the RADB', + cep4_storage_resource['name'], + cep4_storage_resource['id'], + humanreadablesize(available_capacity)) + + radbrpc.updateResourceAvailability(cep4_storage_resource['id'], available_capacity=available_capacity) + except Exception as e: + logger.error(e) + + def open(self): + self.disk_usage.open() + self.event_bus.open() + + self._updateCacheThread = Thread(target=self._scanProjectsTreeAndUpdateOldEntriesInCache) + self._updateCacheThread.daemon = True + self._updateCacheThreadRunning = True + self._updateCacheThread.start() + + self.otdb_listener.start_listening() + self.dm_listener.start_listening() + + def close(self): + self.otdb_listener.stop_listening() + self.dm_listener.stop_listening() + self._updateCacheThreadRunning = False + self._updateCacheThread.join() + + self.event_bus.close() + self.disk_usage.close() + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def onObservationCompleting(self, otdb_id, modificationTime): + self._onDiskActivityForOTDBId(otdb_id) + + def onObservationAborted(self, otdb_id, modificationTime): + self._onDiskActivityForOTDBId(otdb_id) + + def onTaskDeleted(self, otdb_id, paths): + self._onDiskActivityForOTDBId(otdb_id) + + def _onDiskActivityForOTDBId(self, otdb_id): + result = self.disk_usage.getDiskUsageForOTDBId(otdb_id) + self._updateCache(result) + + task_path = result.get('path') + projects_path = self.disk_usage.path_resolver.projects_path + + # update all paths up the tree up to the projects_path + # update the resource availability in the radb as well + path = task_path + while path: + parent_path = '/'.join(path.split('/')[:-1]) + + if projects_path.startswith(parent_path) and len(parent_path) < len(projects_path): + break + + logger.info('invalidating cache entry for %s because disk usage for task %s in %s changed', parent_path, otdb_id, task_path) + + self._invalidateCacheEntryForPath(parent_path) + + path = parent_path + + # trigger update cache thread + self._continueUpdateCacheThread = True + + def getDiskUsageForOTDBId(self, otdb_id, include_scratch_paths=True, force_update=False): + return self.getDiskUsageForTask(otdb_id=otdb_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + def getDiskUsageForMoMId(self, mom_id, include_scratch_paths=True, force_update=False): + return self.getDiskUsageForTask(mom_id=mom_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + def getDiskUsageForRADBId(self, radb_id, include_scratch_paths=True, force_update=False): + return self.getDiskUsageForTask(radb_id=radb_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + def getDiskUsageForTask(self, radb_id=None, mom_id=None, otdb_id=None, include_scratch_paths=True, force_update=False): + logger.info("cache.getDiskUsageForTask(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + + if otdb_id != None and not include_scratch_paths: + with self._cacheLock: + path = self._cache['otdb_id2path'].get(otdb_id) + + if path: + logger.info('Using path from cache for otdb_id %s %s', otdb_id, path) + return self.getDiskUsageForPath(path, force_update=force_update) + + path_result = self.disk_usage.path_resolver.getPathForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, include_scratch_paths=include_scratch_paths) + if path_result['found']: + path_du_result = self.getDiskUsageForPath(path_result['path'], force_update=force_update) + + if 'scratch_paths' in path_result: + path_du_result['scratch_paths'] = {} + + for scratch_path in path_result['scratch_paths']: + scratch_path_du_result = self.getDiskUsageForPath(scratch_path, force_update=force_update) + path_du_result['scratch_paths'][scratch_path] = scratch_path_du_result + + return path_du_result + + return {'found': False, 'path': path_result['path']} + + def getDiskUsageForTasks(self, radb_ids=None, mom_ids=None, otdb_ids=None, include_scratch_paths=True, force_update=False): + logger.info("cache.getDiskUsageForTask(radb_ids=%s, mom_ids=%s, otdb_ids=%s)" % (radb_ids, mom_ids, otdb_ids)) + result = {'radb_ids': {}, 'mom_ids': {}, 'otdb_ids': {}} + + if radb_ids: + for radb_id in radb_ids: + result['radb_ids'][str(radb_id)] = self.getDiskUsageForTask(radb_id=radb_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + if mom_ids: + for mom_id in mom_ids: + result['mom_ids'][str(mom_id)] = self.getDiskUsageForTask(mom_id=mom_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + if otdb_ids: + for otdb_id in otdb_ids: + result['otdb_ids'][str(otdb_id)] = self.getDiskUsageForTask(otdb_id=otdb_id, include_scratch_paths=include_scratch_paths, force_update=force_update) + + return result + + def getDiskUsageForPath(self, path, force_update=False): + logger.info("cache.getDiskUsageForPath(%s)", path) + needs_cache_update = False + with self._cacheLock: + needs_cache_update |= path not in self._cache['path_du_results'] + + if needs_cache_update or force_update: + logger.info("cache update needed for %s", path) + result = du_getDiskUsageForPath(path) + self._updateCache(result) + + with self._cacheLock: + if path in self._cache['path_du_results']: + result = self._cache['path_du_results'][path] + else: + result = { 'found': False, 'path':path, 'message': 'unknown error' } + if not self.disk_usage.path_resolver.pathExists(path): + result['message'] = 'No such path: %s' % path + + logger.info('cache.getDiskUsageForPath result: %s' % result) + return result + + def getDiskUsageForTaskAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, force_update=False): + logger.info("cache.getDiskUsageForTaskAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + task_du_result = self.getDiskUsageForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, force_update=force_update) + if task_du_result['found']: + task_sd_result = self.disk_usage.path_resolver.getSubDirectories(task_du_result['path']) + + if task_sd_result['found']: + subdir_paths = [os.path.join(task_du_result['path'],sd) for sd in task_sd_result['sub_directories']] + + #TODO: potential for parallelization + subdirs_du_result = { sd: self.getDiskUsageForPath(sd, force_update=force_update) for sd in subdir_paths } + + result = {'found':True, 'task_directory': task_du_result, 'sub_directories': subdirs_du_result } + logger.info("result for cache.getDiskUsageForTaskAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s): %s", radb_id, mom_id, otdb_id, result) + return result + + logger.warn("result for cache.getDiskUsageForTaskAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s): %s", radb_id, mom_id, otdb_id, task_du_result) + return task_du_result + + def getDiskUsageForProjectDirAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, project_name=None, force_update=False): + logger.info("cache.getDiskUsageForProjectDirAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + path_result = self.disk_usage.path_resolver.getProjectDirAndSubDirectories(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, project_name=project_name) + if path_result['found']: + projectdir_du_result = self.getDiskUsageForPath(path_result['path'], force_update=force_update) + subdir_paths = [os.path.join(path_result['path'],sd) for sd in path_result['sub_directories']] + + #TODO: potential for parallelization + subdirs_du_result = { sd: self.getDiskUsageForPath(sd, force_update=force_update) for sd in subdir_paths } + result = {'found':True, 'projectdir': projectdir_du_result, 'sub_directories': subdirs_du_result } + logger.info('cache.getDiskUsageForProjectDirAndSubDirectories result: %s' % result) + return result + + return path_result + + def getDiskUsageForProjectsDirAndSubDirectories(self, force_update=False): + logger.info("cache.getDiskUsageForProjectsDirAndSubDirectories") + projects_path = self.disk_usage.path_resolver.projects_path + projectsdir_du_result = self.getDiskUsageForPath(projects_path, force_update=force_update) + + result = {'found':True, 'projectdir': projectsdir_du_result } + + project_subdirs_result = self.disk_usage.path_resolver.getSubDirectories(projects_path) + if project_subdirs_result['found']: + subdir_paths = [os.path.join(projects_path,sd) for sd in project_subdirs_result['sub_directories']] + + #TODO: potential for parallelization + subdirs_du_result = { sd: self.getDiskUsageForPath(sd, force_update=force_update) for sd in subdir_paths } + result['sub_directories'] = subdirs_du_result + + logger.info('cache.getDiskUsageForProjectsDirAndSubDirectories result: %s' % result) + return result + +if __name__ == '__main__': + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + + with CacheManager(broker='scu099.control.lofar') as cm: + waitForInterrupt() diff --git a/SAS/DataManagement/StorageQueryService/config.py b/SAS/DataManagement/StorageQueryService/config.py new file mode 100644 index 0000000000000000000000000000000000000000..8e00e695b47544ec096f4a01fbd1c8588f9678bf --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/config.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +# $Id$ + +from lofar.messaging import adaptNameToEnvironment + +DEFAULT_BUSNAME = adaptNameToEnvironment('lofar.dm.command') +DEFAULT_SERVICENAME = 'StorageQueryService' diff --git a/SAS/DataManagement/StorageQueryService/diskusage.py b/SAS/DataManagement/StorageQueryService/diskusage.py new file mode 100644 index 0000000000000000000000000000000000000000..d5a0063f588dacfe73d7d6695a26b27d30581694 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/diskusage.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# $Id$ + +import logging +import subprocess +import socket +import os.path +from optparse import OptionParser +from lofar.common.util import humanreadablesize + +from lofar.sas.datamanagement.common.config import CEP4_DATA_MOUNTPOINT +from lofar.sas.datamanagement.common.path import PathResolver +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +logger = logging.getLogger(__name__) + +def getDiskUsageForPath(path): + logger.info('getDiskUsageForPath(%s)', path) + cmd = ['rbh-du', '-bd', path] + hostname = socket.gethostname() + if not 'mgmt0' in hostname: + cmd = ['ssh', 'lofarsys@mgmt01.cep4.control.lofar'] + cmd + logger.info(' '.join(cmd)) + + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + + if proc.returncode != 0: + logger.error(out + err) + return {'found': False, 'path': path, 'message': out} + + # example of out + # Using config file '/etc/robinhood.d/tmpfs/tmp_fs_mgr_basic.conf'. + # /data/projects/2016LOFAROBS/L522380 + # dir count:3906, size:16048128, spc_used:16052224 + # file count:17568, size:42274164368, spc_used:42327519232 + + #parse out + lines = [l.strip() for l in out.split('\n')] + file_lines = [l for l in lines if 'file count' in l] + if file_lines: + parts = [p.strip() for p in file_lines[0].split(',')] + partsDict = {p.split(':')[0].strip():p.split(':')[1].strip() for p in parts} + + result = {'found': True, 'disk_usage': None, 'path': path, 'name': path.split('/')[-1]} + + if 'size' in partsDict: + result['disk_usage'] = int(partsDict['size']) + + if 'file count' in partsDict: + result['nr_of_files'] = int(partsDict['file count']) + + result['disk_usage_readable'] = humanreadablesize(result['disk_usage']) + else: + dir_lines = [l for l in lines if 'dir count' in l] + if dir_lines: + result = {'found': True, 'disk_usage': 0, 'nr_of_files': 0, 'path': path, 'name': path.split('/')[-1]} + else: + result = {'found': False, 'path': path } + + try: + path_items = path.rstrip('/').split('/') + if len(path_items) >=3 and path_items[-1].startswith('L') and path_items[-1][1:].isdigit() and 'projects' in path_items[-3]: + logger.info('found path for otdb_id %s %s', path_items[-1][1:], path) + result['otdb_id'] = int(path_items[-1][1:]) + except Exception as e: + logger.error('Could not parse otdb_id from path %s %s', path, e) + + logger.info('returning: %s' % result) + return result + + +class DiskUsage: + def __init__(self, + mountpoint=CEP4_DATA_MOUNTPOINT, + radb_busname=RADB_BUSNAME, + radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + broker=None): + self.path_resolver = PathResolver(mountpoint=mountpoint, + radb_busname=radb_busname, + radb_servicename=radb_servicename, + mom_busname=mom_busname, + mom_servicename=mom_servicename, + broker=broker) + + def open(self): + self.path_resolver.open() + + def close(self): + self.path_resolver.close() + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def getDiskUsageForOTDBId(self, otdb_id): + return self.getDiskUsageForTask(otdb_id=otdb_id) + + def getDiskUsageForMoMId(self, mom_id): + return self.getDiskUsageForTask(mom_id=mom_id) + + def getDiskUsageForRADBId(self, radb_id): + return self.getDiskUsageForTask(radb_id=radb_id) + + def getDiskUsageForTask(self, radb_id=None, mom_id=None, otdb_id=None): + logger.info("getDiskUsageForTask(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + result = self.path_resolver.getPathForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + if result['found']: + return getDiskUsageForPath(result['path']) + + return {'found': False, 'path': result['path']} + + def getDiskUsageForTaskAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None): + logger.info("getDiskUsageForTaskAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + task_du_result = self.getDiskUsageForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + if task_du_result['found']: + task_sd_result = self.path_resolver.getSubDirectoriesForTask(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id) + + if task_sd_result['found']: + subdir_paths = [os.path.join(task_du_result['path'],sd) for sd in task_sd_result['sub_directories']] + + #TODO: potential for parallelization + subdirs_du_result = { sd: getDiskUsageForPath(sd) for sd in subdir_paths } + result = {'found':True, 'task_direcory': task_du_result, 'sub_directories': subdirs_du_result } + logger.info('result: %s' % result) + return result + + return task_du_result + + def getDiskUsageForProjectDirAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, project_name=None): + logger.info("getDiskUsageForProjectDirAndSubDirectories(radb_id=%s, mom_id=%s, otdb_id=%s)" % (radb_id, mom_id, otdb_id)) + path_result = self.path_resolver.getProjectDirAndSubDirectories(radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, project_name=project_name) + if path_result['found']: + projectdir_du_result = getDiskUsageForPath(path_result['path']) + subdir_paths = [os.path.join(path_result['path'],sd) for sd in path_result['sub_directories']] + + #TODO: potential for parallelization + subdirs_du_result = { sd: getDiskUsageForPath(sd) for sd in subdir_paths } + result = {'found':True, 'projectdir': projectdir_du_result, 'sub_directories': subdirs_du_result } + logger.info('result: %s' % result) + return result + + return path_result + +def main(): + # Check the invocation arguments + parser = OptionParser("%prog [options] <path>", + description='get disk usage for (cep4) path') + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO if options.verbose else logging.WARN) + + if len(args) == 0: + parser.print_help() + exit(1) + + result = getDiskUsageForPath(args[0]) + + if result['found']: + print 'path %s' % result['path'] + print 'disk_usage %s %s' % (result['disk_usage'], result['disk_usage_readbale']) + print 'nr_of_files %s' % result['nr_of_files'] + else: + print result['message'] + exit(1) + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/StorageQueryService/rpc.py b/SAS/DataManagement/StorageQueryService/rpc.py new file mode 100644 index 0000000000000000000000000000000000000000..600b3d21d91fef92ca5c15d60a571e05ed51a767 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/rpc.py @@ -0,0 +1,117 @@ +#!/usr/bin/python + +import logging +import qpid +from lofar.messaging.RPC import RPC, RPCException, RPCWrapper +from lofar.sas.datamanagement.storagequery.config import DEFAULT_BUSNAME, DEFAULT_SERVICENAME +from lofar.common.util import humanreadablesize + +logger = logging.getLogger(__name__) + +class StorageQueryRPC(RPCWrapper): + def __init__(self, busname=DEFAULT_BUSNAME, + servicename=DEFAULT_SERVICENAME, + broker=None): + super(StorageQueryRPC, self).__init__(busname, servicename, broker, timeout=18000) + + def _convertTimestamps(self, result): + if isinstance(result, dict): + for k, v in result.items(): + if isinstance(v, dict): + self._convertTimestamps(v) + elif isinstance(v, qpid.datatypes.timestamp): + result[k] = v.datetime() + + return result + + def getPathForOTDBId(self, otdb_id): + return self.rpc('GetPathForOTDBId', otdb_id=otdb_id) + + def getDiskUsageForOTDBId(self, otdb_id, include_scratch_paths=True, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForOTDBId', otdb_id=otdb_id, include_scratch_paths=include_scratch_paths, force_update=force_update)) + + def getDiskUsageForMoMId(self, mom_id, include_scratch_paths=True, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForMoMId', mom_id=mom_id, include_scratch_paths=include_scratch_paths, force_update=force_update)) + + def getDiskUsageForRADBId(self, radb_id, include_scratch_paths=True, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForRADBId', radb_id=radb_id, include_scratch_paths=include_scratch_paths, force_update=force_update)) + + def getDiskUsageForTask(self, radb_id=None, mom_id=None, otdb_id=None, include_scratch_paths=True, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForTask', radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, include_scratch_paths=include_scratch_paths, force_update=force_update)) + + def getDiskUsageForTasks(self, radb_ids=None, mom_ids=None, otdb_ids=None, include_scratch_paths=True, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForTasks', radb_ids=radb_ids, mom_ids=mom_ids, otdb_ids=otdb_ids, include_scratch_paths=include_scratch_paths, force_update=force_update)) + + def getDiskUsageForTaskAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForTaskAndSubDirectories', radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, force_update=force_update)) + + def getDiskUsageForProjectDirAndSubDirectories(self, radb_id=None, mom_id=None, otdb_id=None, project_name=None, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForProjectDirAndSubDirectories', radb_id=radb_id, mom_id=mom_id, otdb_id=otdb_id, project_name=project_name, force_update=force_update)) + + def getDiskUsageForProjectsDirAndSubDirectories(self, force_update=False): + return self._convertTimestamps(self.rpc('GetDiskUsageForProjectsDirAndSubDirectories', force_update=force_update)) + +def main(): + import sys + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser('%prog [options]', + description='do storage queries (on cep4) from the commandline') + parser.add_option('-o', '--otdb_id', dest='otdb_id', type='int', default=None, help='otdb_id of task to get the disk usage for') + parser.add_option('-m', '--mom_id', dest='mom_id', type='int', default=None, help='mom_id of task to get the disk usage for') + parser.add_option('-r', '--radb_id', dest='radb_id', type='int', default=None, help='radb_id of task to get the disk usage for') + parser.add_option('-s', '--subdirs', dest='subdirs', action='store_true', help='get the disk usage of the task and its sub directories for the given otdb_id/mom_id/radb_id') + parser.add_option('-p', '--project', dest='project', type='string', default=None, help='get the disk usage of the project path and all its sub directories for the given project name') + parser.add_option('-P', '--projects', dest='projects', action='store_true', help='get the disk usage of the projects path and all its projects sub directories') + parser.add_option('-f', '--force_update', dest='force_update', action='store_true', help='force an update of the cache with a new du call') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option('--busname', dest='busname', type='string', default=DEFAULT_BUSNAME, help='Name of the bus exchange on the qpid broker, default: %s' % DEFAULT_BUSNAME) + parser.add_option('--servicename', dest='servicename', type='string', default=DEFAULT_SERVICENAME, help='Name for this service, default: %s' % DEFAULT_SERVICENAME) + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + if not (options.otdb_id or options.mom_id or options.radb_id or options.project or options.projects): + parser.print_help() + exit(1) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.INFO if options.verbose else logging.WARN) + + with StorageQueryRPC(busname=options.busname, servicename=options.servicename, broker=options.broker) as rpc: + if options.projects: + result = rpc.getDiskUsageForProjectsDirAndSubDirectories(force_update=options.force_update) + if result['found']: + import pprint + pprint.pprint(result) + else: + print result['message'] + exit(1) + elif options.project: + result = rpc.getDiskUsageForProjectDirAndSubDirectories(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id, project_name=options.project, force_update=options.force_update) + if result['found']: + import pprint + pprint.pprint(result) + else: + print result['message'] + exit(1) + elif options.subdirs: + result = rpc.getDiskUsageForTaskAndSubDirectories(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id, force_update=options.force_update) + if result['found']: + import pprint + pprint.pprint(result) + else: + print result['message'] + exit(1) + else: + result = rpc.getDiskUsageForTask(otdb_id=options.otdb_id, mom_id=options.mom_id, radb_id=options.radb_id, force_update=options.force_update) + if result['found']: + print 'path %s' % result['path'] + print 'disk_usage %s %s' % (result.get('disk_usage'), result.get('disk_usage_readable')) + print 'nr_of_files %s' % result.get('nr_of_files') + else: + print result['message'] + exit(1) + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/StorageQueryService/service.py b/SAS/DataManagement/StorageQueryService/service.py new file mode 100644 index 0000000000000000000000000000000000000000..953937cb03f4912ec3aef9bae5463b784ea7aec0 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/service.py @@ -0,0 +1,110 @@ +#!/usr/bin/python +# $Id$ + +import logging +import subprocess +import socket +import os.path +from optparse import OptionParser +from lofar.messaging import Service +from lofar.messaging.Service import MessageHandlerInterface +from lofar.messaging import setQpidLogLevel +from lofar.common.util import waitForInterrupt + +from lofar.messaging.Service import MessageHandlerInterface +from lofar.sas.datamanagement.common.config import CEP4_DATA_MOUNTPOINT +from lofar.sas.datamanagement.storagequery.cache import CacheManager +from lofar.sas.datamanagement.storagequery.diskusage import DiskUsage +from lofar.sas.datamanagement.storagequery.config import DEFAULT_BUSNAME, DEFAULT_SERVICENAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +logger = logging.getLogger(__name__) + +class StorageQueryHandler(MessageHandlerInterface): + def __init__(self, + mountpoint=CEP4_DATA_MOUNTPOINT, + radb_busname=RADB_BUSNAME, + radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + broker=None, + **kwargs): + + self.cache = kwargs.pop('cache_manager') + + super(StorageQueryHandler, self).__init__(**kwargs) + self.disk_usage = DiskUsage(mountpoint=mountpoint, + radb_busname=radb_busname, + radb_servicename=radb_servicename, + mom_busname=mom_busname, + mom_servicename=mom_servicename, + broker=broker) + + self.service2MethodMap = {'GetPathForOTDBId': self.cache.disk_usage.path_resolver.getPathForOTDBId, + 'GetDiskUsageForOTDBId': self.cache.getDiskUsageForOTDBId, + 'GetDiskUsageForMoMId': self.cache.getDiskUsageForMoMId, + 'GetDiskUsageForRADBId': self.cache.getDiskUsageForRADBId, + 'GetDiskUsageForTask': self.cache.getDiskUsageForTask, + 'GetDiskUsageForTasks': self.cache.getDiskUsageForTasks, + 'GetDiskUsageForTaskAndSubDirectories': self.cache.getDiskUsageForTaskAndSubDirectories, + 'GetDiskUsageForProjectDirAndSubDirectories': self.cache.getDiskUsageForProjectDirAndSubDirectories, + 'GetDiskUsageForProjectsDirAndSubDirectories': self.cache.getDiskUsageForProjectsDirAndSubDirectories} + +def createService(busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, broker=None, + mountpoint=CEP4_DATA_MOUNTPOINT, verbose=False, + radb_busname=RADB_BUSNAME, radb_servicename=RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + cache_manager=None): + return Service(servicename, + StorageQueryHandler, + busname=busname, + broker=broker, + use_service_methods=True, + numthreads=4, + verbose=verbose, + handler_args={'mountpoint': mountpoint, + 'radb_busname':RADB_BUSNAME, + 'radb_servicename':RADB_SERVICENAME, + 'mom_busname':DEFAULT_MOMQUERY_BUSNAME, + 'mom_servicename':DEFAULT_MOMQUERY_SERVICENAME, + 'broker':broker, + 'cache_manager':cache_manager}) + +def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + + # Check the invocation arguments + parser = OptionParser("%prog [options]", + description='runs the storagequery service') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option("-b", "--busname", dest="busname", type="string", default=DEFAULT_BUSNAME, help="Name of the bus exchange on the qpid broker, default: %s" % DEFAULT_BUSNAME) + parser.add_option("-s", "--servicename", dest="servicename", type="string", default=DEFAULT_SERVICENAME, help="Name for this service, default: %s" % DEFAULT_SERVICENAME) + parser.add_option("--radb_busname", dest="radb_busname", type="string", default=RADB_BUSNAME, help="Name of the bus on which the RADB service listens, default: %default") + parser.add_option("--radb_servicename", dest="radb_servicename", type="string", default=RADB_SERVICENAME, help="Name of the RADB service, default: %default") + parser.add_option("--mom_busname", dest="mom_busname", type="string", default=DEFAULT_MOMQUERY_BUSNAME, help="Name of the bus on which the MoM service listens, default: %default") + parser.add_option("--mom_servicename", dest="mom_servicename", type="string", default=DEFAULT_MOMQUERY_SERVICENAME, help="Name of the MoM service, default: %default") + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.DEBUG if options.verbose else logging.INFO) + setQpidLogLevel(logging.INFO) + + with CacheManager(broker=options.broker) as cache_manager: + with createService(busname=options.busname, + servicename=options.servicename, + broker=options.broker, + verbose=options.verbose, + radb_busname=options.radb_busname, + radb_servicename=options.radb_servicename, + mom_busname=options.mom_busname, + mom_servicename=options.mom_busname, + cache_manager=cache_manager): + waitForInterrupt() + +if __name__ == '__main__': + main() diff --git a/SAS/DataManagement/StorageQueryService/storagequery b/SAS/DataManagement/StorageQueryService/storagequery new file mode 100755 index 0000000000000000000000000000000000000000..6eb5904ac62a09cef04dd94e5dffecb051940a6c --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/storagequery @@ -0,0 +1,9 @@ +#!/usr/bin/python + +''' +do storage queries (on cep4) from the commandline +''' + +if __name__ == '__main__': + from lofar.sas.datamanagement.storagequery.rpc import main + main() diff --git a/SAS/DataManagement/StorageQueryService/storagequeryservice b/SAS/DataManagement/StorageQueryService/storagequeryservice new file mode 100755 index 0000000000000000000000000000000000000000..d33f5fee03b989335d7b66b0488ef6d403bc72f2 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/storagequeryservice @@ -0,0 +1,10 @@ +#!/usr/bin/python +# $Id: radbservice 33373 2016-01-22 11:01:15Z schaap $ + +''' +runs the storagequery service +''' + +if __name__ == '__main__': + from lofar.sas.datamanagement.storagequery.service import main + main() diff --git a/SAS/DataManagement/StorageQueryService/storagequeryservice.ini b/SAS/DataManagement/StorageQueryService/storagequeryservice.ini new file mode 100644 index 0000000000000000000000000000000000000000..d65bbc1241316fd088e1f7dbad99e15d9db0b773 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/storagequeryservice.ini @@ -0,0 +1,8 @@ +[program:storagequeryservice] +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec storagequeryservice' +user=lofarsys +stopsignal=INT ; KeyboardInterrupt +stopasgroup=true ; bash does not propagate signals +stdout_logfile=%(program_name)s.log +redirect_stderr=true +stderr_logfile=NONE diff --git a/SAS/DataManagement/StorageQueryService/test/CMakeLists.txt b/SAS/DataManagement/StorageQueryService/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2e74c54f0c7563424bb586fcdd913e113f0af0c6 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/test/CMakeLists.txt @@ -0,0 +1,5 @@ +# $Id$ +include(LofarCTest) + +lofar_add_test(test_storagequery_service_and_rpc) + diff --git a/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.py b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.py new file mode 100755 index 0000000000000000000000000000000000000000..b62c2a3078b5fe9203ddab003a0f1c2679f593d7 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import unittest +import uuid +import datetime +import logging +from lofar.messaging import Service +from lofar.sas.datamanagement.storagequery.service import createService +from lofar.sas.datamanagement.storagequery.rpc import StorageQueryRPC +from lofar.sas.datamanagement.storagequery.cache import CacheManager +from qpid.messaging.exceptions import * + +try: + from qpid.messaging import Connection + from qpidtoollibs import BrokerAgent +except ImportError: + print 'Cannot run test without qpid tools' + print 'Please source qpid profile' + exit(3) + +connection = None +broker = None + +try: + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + logger = logging.getLogger(__name__) + + # setup broker connection + connection = Connection.establish('127.0.0.1') + broker = BrokerAgent(connection) + + # add test service busname + busname = 'test-lofarbus-%s' % (uuid.uuid1()) + broker.addExchange('topic', busname) + + class TestCleanupServiceAndRPC(unittest.TestCase): + def test(self): + '''basic test ''' + rpc = StorageQueryRPC(busname=busname) + #self.assertEqual('foo', rpc.foo()) + + # TODO: fix test + ## create and run the service + #with CacheManager(busname=busname) as cache_manager: + #with createService(busname=busname, cache_manager=cache_manager): + ## and run all tests + #unittest.main() + +except ConnectError as ce: + logger.error(ce) + exit(3) +finally: + # cleanup test bus and exit + if broker: + broker.delExchange(busname) + if connection: + connection.close() diff --git a/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.run b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.run new file mode 100755 index 0000000000000000000000000000000000000000..10b5e4b507ca0e815286e9c5ee75b3c4009df503 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.run @@ -0,0 +1,6 @@ +#!/bin/bash + +# Run the unit test +source python-coverage.sh +python_coverage_test "storagequery*" test_storagequery_service_and_rpc.py + diff --git a/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.sh b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.sh new file mode 100755 index 0000000000000000000000000000000000000000..86a85a94fab9710fb5b9dcfa8716c7554ade18c6 --- /dev/null +++ b/SAS/DataManagement/StorageQueryService/test/test_storagequery_service_and_rpc.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./runctest.sh test_storagequery_service_and_rpc diff --git a/SAS/MoM/MoMQueryService/CMakeLists.txt b/SAS/MoM/MoMQueryService/CMakeLists.txt index bb355d1c6eb212e65a497927f7da6a23b6f35245..b16906069d39c80990429db49b1b95a1589a6974 100644 --- a/SAS/MoM/MoMQueryService/CMakeLists.txt +++ b/SAS/MoM/MoMQueryService/CMakeLists.txt @@ -9,11 +9,12 @@ set(_py_files config.py momqueryservice.py momqueryrpc.py + momrpc.py ) python_install(${_py_files} DESTINATION lofar/mom/momqueryservice) -lofar_add_bin_scripts(momqueryservice momquery) +lofar_add_bin_scripts(momqueryservice momquery momcopytask) # supervisord config files install(FILES diff --git a/SAS/MoM/MoMQueryService/config.py b/SAS/MoM/MoMQueryService/config.py index a5b66e022d8f044d04607ee5637fa55e1c0adebc..7067fe985935b5da1e67b790741e0168c4efa05f 100644 --- a/SAS/MoM/MoMQueryService/config.py +++ b/SAS/MoM/MoMQueryService/config.py @@ -5,3 +5,6 @@ from lofar.messaging import adaptNameToEnvironment DEFAULT_MOMQUERY_BUSNAME = adaptNameToEnvironment('lofar.ra.command') DEFAULT_MOMQUERY_SERVICENAME = 'momqueryservice' + +DEFAULT_MOM_BUSNAME = adaptNameToEnvironment('lofar.mom.command') +DEFAULT_MOM_SERVICENAME = '' diff --git a/SAS/MoM/MoMQueryService/momcopytask b/SAS/MoM/MoMQueryService/momcopytask new file mode 100644 index 0000000000000000000000000000000000000000..74208d5e5b9f6c45c71e3684aa994918fb126fbd --- /dev/null +++ b/SAS/MoM/MoMQueryService/momcopytask @@ -0,0 +1,11 @@ +#!/usr/bin/python +# $Id: $ + +''' +performs an rpc call to the mom copy task service +''' + +from lofar.mom.momqueryservice.momrpc import main + +if __name__ == '__main__': + main() diff --git a/SAS/MoM/MoMQueryService/momquery b/SAS/MoM/MoMQueryService/momquery old mode 100644 new mode 100755 diff --git a/SAS/MoM/MoMQueryService/momqueryrpc.py b/SAS/MoM/MoMQueryService/momqueryrpc.py index b9a9548f8d68f99d8eb5e5e3ee0c466075f58949..99a51e572682610db215e2add9c4a3d88a581f99 100644 --- a/SAS/MoM/MoMQueryService/momqueryrpc.py +++ b/SAS/MoM/MoMQueryService/momqueryrpc.py @@ -2,6 +2,7 @@ import sys import logging +import pprint from optparse import OptionParser from lofar.messaging.RPC import RPC, RPCException, RPCWrapper from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME @@ -13,6 +14,12 @@ logger = logging.getLogger(__file__) class MoMQueryRPC(RPCWrapper): + def __init__(self, busname=DEFAULT_MOMQUERY_BUSNAME, + servicename=DEFAULT_MOMQUERY_SERVICENAME, + broker=None, + timeout=120): + super(MoMQueryRPC, self).__init__(busname, servicename, broker, timeout=timeout) + def getProjectDetails(self, ids): '''get the project details for one or more mom ids :param ids single or list of mom ids @@ -38,6 +45,48 @@ class MoMQueryRPC(RPCWrapper): logger.info("Received %s projects" % (len(projects))) return projects + def getProject(self, project_mom2id): + '''get projects by mo2_id''' + logger.info("getProject(%s)", project_mom2id) + project = self.rpc('GetProject', project_mom2id=project_mom2id) + return project + + def getProjectTaskIds(self, project_mom2id): + '''get all task mom2id's for the given project + :rtype dict with all projects''' + logger.info("getProjectTaskIds") + task_ids = self.rpc('GetProjectTaskIds', project_mom2id=project_mom2id) + return task_ids + + def getPredecessorIds(self, ids): + logger.debug("getSuccessorIds(%s)", ids) + result = self.rpc('GetPredecessorIds', mom_ids=ids) + logger.info("GetPredecessorIds(%s): %s", ids, result) + return result + + def getSuccessorIds(self, ids): + logger.debug("getSuccessorIds(%s)", ids) + result = self.rpc('GetSuccessorIds', mom_ids=ids) + logger.info("getSuccessorIds(%s): %s", ids, result) + return result + + def getTaskIdsInGroup(self, mom_group_ids): + logger.debug("getTaskIdsInGroup(%s)", mom_group_ids) + result = self.rpc('GetTaskIdsInGroup', mom_group_ids=mom_group_ids) + logger.info("getTaskIdsInGroup(%s): %s", mom_group_ids, result) + return result + + def getTaskIdsInParentGroup(self, mom_parent_group_ids): + logger.debug("getTaskIdsInParentGroup(%s)", mom_parent_group_ids) + result = self.rpc('GetTaskIdsInParentGroup', mom_parent_group_ids=mom_parent_group_ids) + logger.info("getTaskIdsInParentGroup(%s): %s", mom_parent_group_ids, result) + return result + + def getDataProducts(self, ids): + logger.debug("getDataProducts(%s)", ids) + result = self.rpc('GetDataProducts', mom_ids=ids) + logger.info('Found # dataproducts per mom2id: %s', ', '.join('%s:%s' % (id, len(dps)) for id, dps in result.items())) + return result def main(): # Check the invocation arguments @@ -49,6 +98,11 @@ def main(): parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') parser.add_option('-P', '--projects', dest='projects', action='store_true', help='get list of all projects') parser.add_option('-p', '--project_details', dest='project_details', type='int', help='get project details for mom object with given id') + parser.add_option('--predecessors', dest='id_for_predecessors', type='int', help='get the predecessor id\'s for the given mom2id') + parser.add_option('--successors', dest='id_for_successors', type='int', help='get the successors id\'s for the given mom2id') + parser.add_option('-g', '--group', dest='group_id', type='int', help='get the tasks ids in the given group mom2id') + parser.add_option('--parent_group', dest='parent_group_id', type='int', help='get the tasks ids in the given parent group mom2id') + parser.add_option('-d', '--dataproducts', dest='id_for_dataproducts', type='int', help='get the dataproducts for the given mom2id') (options, args) = parser.parse_args() if len(sys.argv) == 1: @@ -71,5 +125,46 @@ def main(): else: print 'No results' + if options.id_for_predecessors: + predecessor_ids = rpc.getPredecessorIds(options.id_for_predecessors) + if predecessor_ids: + for k, v in predecessor_ids.items(): + print ' %s: %s' % (k, v) + else: + print 'No results' + + if options.id_for_successors: + successor_ids = rpc.getSuccessorIds(options.id_for_successors) + if successor_ids: + for k, v in successor_ids.items(): + print ' %s: %s' % (k, v) + else: + print 'No results' + + if options.group_id: + task_ids = rpc.getTaskIdsInGroup(options.group_id) + if task_ids: + for k, v in task_ids.items(): + print ' %s: %s' % (k, v) + else: + print 'No results' + + if options.parent_group_id: + task_ids = rpc.getTaskIdsInParentGroup(options.parent_group_id) + if task_ids: + for k, v in task_ids.items(): + print ' %s: %s' % (k, v) + else: + print 'No results' + + if options.id_for_dataproducts: + results = rpc.getDataProducts(options.id_for_dataproducts) + if results: + for mom2id, dps in results.items(): + print ' dataproducts for %s' % mom2id + pprint.pprint(dps) + else: + print 'No results' + if __name__ == '__main__': main() diff --git a/SAS/MoM/MoMQueryService/momqueryservice b/SAS/MoM/MoMQueryService/momqueryservice old mode 100644 new mode 100755 diff --git a/SAS/MoM/MoMQueryService/momqueryservice.py b/SAS/MoM/MoMQueryService/momqueryservice.py index 5893a21bc8be99bece420655fab9ef4a13d51ede..2b57e1744fa480f32a0e05b4ba69f5183a73a566 100755 --- a/SAS/MoM/MoMQueryService/momqueryservice.py +++ b/SAS/MoM/MoMQueryService/momqueryservice.py @@ -55,6 +55,20 @@ def _isListOfInts(items): return True +def _toIdsString(mom_ids): + if isinstance(mom_ids, int): + ids = [mom_ids] + elif _isListOfInts(mom_ids): + ids = mom_ids + else: + ids = _idsFromString(mom_ids) + + if not ids: + raise ValueError("Could not find proper ids in: " + mom_ids) + + ids_str = ','.join([str(id) for id in ids]) + return ids_str + class MoMDatabaseWrapper: '''handler class for details query in mom db''' def __init__(self, dbcreds): @@ -92,38 +106,35 @@ class MoMDatabaseWrapper: def getProjectDetails(self, mom_ids): ''' get the project details (project_mom2id, project_name, project_description, object_mom2id, object_name, object_description, - object_type, object_group_id) for given mom object mom_ids + object_type, object_group_id, object_group_name, object_status) for given mom object mom_ids :param mixed mom_ids comma seperated string of mom2object id's, or list of ints :rtype list of dict's key value pairs with the project details ''' if not mom_ids: return {} - if _isListOfInts(mom_ids): - ids = mom_ids - else: - ids = _idsFromString(mom_ids) - - if not ids: - raise ValueError("Could not find proper ids in: " + mom_ids) + ids_str = _toIdsString(mom_ids) - ids_str = ','.join([str(id) for id in ids]) - - logger.info("Query for mom id%s: %s" % - ('\'s' if len(ids) > 1 else '', ids_str)) + logger.info("getProjectDetails for mom ids: %s" % ids_str) # TODO: make a view for this query in momdb! query = '''SELECT project.mom2id as project_mom2id, project.id as project_mom2objectid, project.name as project_name, project.description as project_description, - object.mom2id as object_mom2id, object.id as object_mom2objectid, object.name as object_name, object.description as object_description, object.mom2objecttype as object_type, object.group_id as object_group_id + object.mom2id as object_mom2id, object.id as object_mom2objectid, object.name as object_name, object.description as object_description, object.mom2objecttype as object_type, status.code as object_status, + object.group_id as object_group_id, grp.id as object_group_mom2objectid, grp.name as object_group_name, grp.description as object_group_description, + parent_grp.id as parent_group_mom2objectid, parent_grp.mom2id as parent_group_mom2id, parent_grp.name as parent_group_name, parent_grp.description as parent_group_description FROM mom2object as object left join mom2object as project on project.id = object.ownerprojectid + left join mom2object as grp on grp.mom2id = object.group_id + left join mom2objectstatus as mostatus on object.currentstatusid = mostatus.id + inner join status on mostatus.statusid = status.id + left join mom2object as parent_grp on parent_grp.id = grp.parentid where object.mom2id in (%s) order by project_mom2id ''' % (ids_str,) rows = self._executeQuery(query) logger.info("Found %d results for mom id(s): %s" % - (len(rows), ids_str)) + (len(rows) if rows else 0, ids_str)) result = {} for row in rows: @@ -156,6 +167,221 @@ class MoMDatabaseWrapper: return result + def getProject(self, project_mom2id): + ''' get project for the given project_mom2id with columns (project_mom2id, project_name, + project_description, status_name, status_id, last_user_id, + last_user_name, statustime) + ''' + ids_str = _toIdsString(mom_ids) + + # TODO: make a view for this query in momdb! + query = '''SELECT project.mom2id as mom2id, project.name as name, project.description as description, + statustype.code as status_name, statustype.id as status_id, + status.userid as last_user_id, status.name as last_user_name, status.statustime as statustime + FROM mom2object as project + left join mom2objectstatus as status on project.currentstatusid = status.id + left join status as statustype on status.statusid=statustype.id + where project.mom2objecttype='PROJECT' and project.mom2id = %s + order by mom2id; + ''' % (ids_str) + result = self._executeQuery(query) + + return result + + def getProjectTaskIds(self, project_mom2id): + if not project_mom2id: + return {} + + ids_str = _toIdsString(project_mom2id) + + logger.info("getProjectTaskIds for project_mom2id: %s" % ids_str) + + query = '''SELECT tasks.mom2id FROM mom2object tasks + inner join mom2object project on project.id = tasks.ownerprojectid + where project.mom2id = %s and + (tasks.mom2objecttype = 'LOFAR_OBSERVATION' or tasks.mom2objecttype like \'%%PIPELINE%%\');''' % ids_str + + rows = self._executeQuery(query) + + result = { 'project_mom2id': project_mom2id, 'task_mom2ids': [r['mom2id'] for r in rows]} + + logger.info('task ids for project: %s', result) + + return result + + def getPredecessorIds(self, mom_ids): + if not mom_ids: + return {} + + ids_str = _toIdsString(mom_ids) + + logger.info("getPredecessorIds for mom ids: %s" % ids_str) + + query = '''SELECT mom2id, predecessor + FROM mom2object + where mom2id in (%s) + order by mom2id; + ''' % (ids_str,) + rows = self._executeQuery(query) + + result = {} + for row in rows: + mom2id = row['mom2id'] + pred_string = row['predecessor'] + pred_id_list = [y[1:] for y in [x.strip() for x in pred_string.split(',')] if y[0] == 'M'] if pred_string else [] + pred_id_list = [int(x) for x in pred_id_list if x.isdigit()] + result[str(mom2id)] = pred_id_list + + for mom2id in ids_str.split(','): + if not mom2id in result: + result[mom2id] = [] + + logger.info('predecessors: %s', result) + + return result + + def getSuccessorIds(self, mom_ids): + if not mom_ids: + return {} + + ids_str = _toIdsString(mom_ids) + + logger.info("getSuccessorIds for mom ids: %s" % ids_str) + + condition = ' OR '.join(['predecessor LIKE \'%%M%s%%\'' % x for x in ids_str.split(',')]) + + # TODO: make a view for this query in momdb! + query = '''SELECT mom2id, predecessor + FROM mom2object + where %s + order by mom2id; + ''' % (condition,) + rows = self._executeQuery(query) + + result = {} + for mom2id in ids_str.split(','): + result[mom2id] = [] + + for row in rows: + suc_mom2id = row['mom2id'] + pred_string = row['predecessor'] + pred_id_list = [y[1:] for y in [x.strip() for x in pred_string.split(',')] if y[0] == 'M'] if pred_string else [] + for mom2id in ids_str.split(','): + if mom2id in pred_id_list: + result[str(mom2id)].append(suc_mom2id) + + logger.info('successors: %s', result) + + return result + + def getTaskIdsInGroup(self, mom_group_ids): + if not mom_group_ids: + return {} + + ids_str = _toIdsString(mom_group_ids) + + logger.info("getTaskIdsInGroup for mom group ids: %s" % ids_str) + + query = '''SELECT mom2id, group_id FROM mom2object + where group_id in (%s) + and (mom2objecttype = 'LOFAR_OBSERVATION' or mom2objecttype like \'%%PIPELINE%%\')''' % ids_str + + rows = self._executeQuery(query) + + result = {} + for group_id in ids_str.split(','): + result[group_id] = [] + + for row in rows: + mom2id = row['mom2id'] + group_id = row['group_id'] + result[str(group_id)].append(mom2id) + + logger.info('task ids per group: %s', result) + + return result + + def getGroupsInParentGroup(self, mom_parent_group_ids): + if not mom_parent_group_ids: + return {} + + ids_str = _toIdsString(mom_parent_group_ids) + + logger.debug("getGroupsInParentGroup for mom parent group ids: %s" % ids_str) + + query = '''SELECT parent.id as parent_mom2object_id, parent.mom2id as parent_mom2id, + grp.mom2id as group_mom2id, grp.id as group_mom2object_id, grp.name as group_name, grp.description as group_description + from mom2object parent + inner join mom2object grp on parent.id = grp.parentid + where parent.mom2id in (%s) + and grp.group_id = grp.mom2id''' % ids_str + + rows = self._executeQuery(query) + + result = {} + for parent_group_id in ids_str.split(','): + result[parent_group_id] = [] + + for row in rows: + parent_group_id = row['parent_mom2id'] + result[str(parent_group_id)].append(row) + + logger.info("getGroupsInParentGroup result: %s", result) + + return result + + def getTaskIdsInParentGroup(self, mom_parent_group_ids): + if not mom_parent_group_ids: + return {} + + ids_str = _toIdsString(mom_parent_group_ids) + + logger.debug("getTaskIdsInParentGroup for mom parent group ids: %s" % ids_str) + + groups_result = self.getGroupsInParentGroup(ids_str) + + result = {} + for parent_mom2id, groups in groups_result.items(): + task_mom2ids_for_parent = set() + group_ids = [x['group_mom2id'] for x in groups] + group_tasks_id_result = self.getTaskIdsInGroup(group_ids) + for group_id, task_mom2ids in group_tasks_id_result.items(): + task_mom2ids_for_parent |= set(task_mom2ids) + + result[parent_mom2id] = list(task_mom2ids_for_parent) + + logger.info('getTaskIdsInParentGroup: %s', result) + + return result + + def getDataProducts(self, mom_ids): + if not mom_ids: + return {} + + ids_str = _toIdsString(mom_ids) + + logger.info("getDataProducts for mom ids: %s" % ids_str) + + query = '''SELECT mo.id as momobject_id, mo.mom2id as mom2id, dp.id, dp.name, dp.exported, dp.status, dp.fileformat + FROM mom2object mo + inner join dataproduct dp on mo.id = dp.mom2objectid + where mo.mom2id in (%s) + and not isnull(dp.fileformat)''' % ids_str + + rows = self._executeQuery(query) + + result = {} + for mom2id in ids_str.split(','): + result[mom2id] = [] + + for row in rows: + mom2id = row['mom2id'] + result[str(mom2id)].append(dict(row)) + + for mom2id, dps in result.items(): + logger.info('Found %s dataproducts for mom2id %s', len(dps), mom2id) + + return result class ProjectDetailsQueryHandler(MessageHandlerInterface): '''handler class for details query in mom db @@ -167,24 +393,47 @@ class ProjectDetailsQueryHandler(MessageHandlerInterface): self.service2MethodMap = { 'GetProjects': self.getProjects, - 'GetProjectDetails': self.getProjectDetails + 'GetProject': self.getProject, + 'GetProjectDetails': self.getProjectDetails, + 'GetPredecessorIds': self.getPredecessorIds, + 'GetSuccessorIds': self.getSuccessorIds, + 'GetTaskIdsInGroup': self.getTaskIdsInGroup, + 'GetTaskIdsInParentGroup': self.getTaskIdsInParentGroup, + 'GetDataProducts': self.getDataProducts, + 'GetProjectTaskIds': self.getProjectTaskIds } def prepare_loop(self): self.momdb = MoMDatabaseWrapper(self.dbcreds) def getProjectDetails(self, mom_ids): - if not mom_ids: - return {} - - ids = _idsFromString(mom_ids) - if not _isListOfInts(ids): - raise ValueError("%s is not a proper list of ints" % str(mom_ids)) - return self.momdb.getProjectDetails(ids) + return self.momdb.getProjectDetails(mom_ids) def getProjects(self): return self.momdb.getProjects() + def getProjectTaskIds(self, project_mom2id): + return self.momdb.getProjectTaskIds(project_mom2id) + + def getProject(self, project_mom2id): + return self.momdb.getProject(project_mom2id) + + def getPredecessorIds(self, mom_ids): + return self.momdb.getPredecessorIds(mom_ids) + + def getSuccessorIds(self, mom_ids): + return self.momdb.getSuccessorIds(mom_ids) + + def getTaskIdsInGroup(self, mom_group_ids): + return self.momdb.getTaskIdsInGroup(mom_group_ids) + + def getTaskIdsInParentGroup(self, mom_parent_group_ids): + return self.momdb.getTaskIdsInParentGroup(mom_parent_group_ids) + + def getDataProducts(self, mom_ids): + return self.momdb.getDataProducts(mom_ids) + + def createService(busname=DEFAULT_MOMQUERY_BUSNAME, servicename=DEFAULT_MOMQUERY_SERVICENAME, dbcreds=None, @@ -203,7 +452,7 @@ def createService(busname=DEFAULT_MOMQUERY_BUSNAME, return Service(servicename, handler, busname=busname, - numthreads=2, + numthreads=8, use_service_methods=True, verbose=False, broker=broker, @@ -225,6 +474,8 @@ def main(): dbcreds = dbcredentials.parse_options(options) + logger.info("Using dbcreds: %s" % dbcreds.stringWithHiddenPassword()) + # start the service and listen. with createService(busname=options.busname, servicename=options.servicename, diff --git a/SAS/MoM/MoMQueryService/momrpc.py b/SAS/MoM/MoMQueryService/momrpc.py new file mode 100644 index 0000000000000000000000000000000000000000..d8800f2d2bffbca5666e43ff3f0b8dc412020067 --- /dev/null +++ b/SAS/MoM/MoMQueryService/momrpc.py @@ -0,0 +1,46 @@ +#!/usr/bin/python + +import sys +import logging +from optparse import OptionParser +from lofar.messaging.RPC import RPC, RPCException, RPCWrapper +from lofar.messaging import setQpidLogLevel +from lofar.mom.momqueryservice.config import DEFAULT_MOM_BUSNAME, DEFAULT_MOM_SERVICENAME + +logger = logging.getLogger(__file__) + +class MoMRPC(RPCWrapper): + def __init__(self, busname=DEFAULT_MOM_BUSNAME, servicename=DEFAULT_MOM_SERVICENAME, broker=None, timeout=120, verbose=False): + super(MoMRPC, self).__init__(busname=busname, servicename=servicename, broker=broker, timeout=timeout, verbose=verbose) + + def copyTask(self, mom2id, newTaskName=None, newTaskDescription=None): + logger.info("calling copyTask rpc for mom2id %s" % (mom2id)) + new_task_mom2id = self.rpc('TaskCopy', mom2Id=mom2id) #, newTaskName=newTaskName, newTaskDescription=newTaskDescription) + logger.info("mom2id of copied task = %s" % (new_task_mom2id)) + return new_task_mom2id + +def main(): + # Check the invocation arguments + parser = OptionParser('%prog [options]', + description='do rpc calls to the momservice from the commandline') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option('-b', '--busname', dest='busname', type='string', default=DEFAULT_MOM_BUSNAME, help='Name of the bus exchange on the qpid broker [default: %default]') + parser.add_option('--mom2id', dest='mom2id_to_copy', type='int', help='[REQUIRED] mom2id of the task to copy.') + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + if options.mom2id_to_copy == None: + parser.print_help() + parser.error('Missing required option mom2id') + + verbose = bool(options.verbose) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.DEBUG if verbose else logging.INFO) + setQpidLogLevel(logging.WARN) + + with MoMRPC(busname=options.busname, broker=options.broker, verbose=verbose) as rpc: + print rpc.copyTask(options.mom2id_to_copy) + +if __name__ == '__main__': + main() diff --git a/SAS/MoM/MoMQueryService/test/test_momqueryservice.py b/SAS/MoM/MoMQueryService/test/test_momqueryservice.py index d9a918f8db60e038fd2aca3e0e0bbb033e3a9334..3d60a9a19aedca7e65b160bc9f310eb26128f0e5 100755 --- a/SAS/MoM/MoMQueryService/test/test_momqueryservice.py +++ b/SAS/MoM/MoMQueryService/test/test_momqueryservice.py @@ -67,13 +67,7 @@ try: self.assertTrue('project_name' in result[testid]) self.assertTrue('project_description' in result[testid]) - def testSqlInjection(self): - inj_testid = testid + '; select * from lofar_mom3.mom2object;' - - with self.assertRaises(ValueError) as error: - result = momrpc.getProjectDetails(inj_testid) - - unittest.main(verbosity=2) + unittest.main() finally: # cleanup test bus and exit diff --git a/SAS/OTDB/sql/getTreeGroup_func.sql b/SAS/OTDB/sql/getTreeGroup_func.sql index b1b1dc95e23e548a1f9c26d4c5f90e945732165f..053363ad9aea96099ca1572ce9da4f967dc96a48 100644 --- a/SAS/OTDB/sql/getTreeGroup_func.sql +++ b/SAS/OTDB/sql/getTreeGroup_func.sql @@ -61,6 +61,7 @@ CREATE OR REPLACE FUNCTION getTreeGroup(INT, INT, VARCHAR(20)) vWhere TEXT; vQuery TEXT; vSortOrder TEXT; + vSortOrderExcept TEXT; vExcept TEXT; vCluster TEXT; @@ -113,6 +114,8 @@ CREATE OR REPLACE FUNCTION getTreeGroup(INT, INT, VARCHAR(20)) END IF; END IF; + vSortOrderExcept := replace(vSortOrder, 't.', ''); + -- do selection FOR vRecord IN EXECUTE ' WITH VICtrees AS ( @@ -140,7 +143,7 @@ CREATE OR REPLACE FUNCTION getTreeGroup(INT, INT, VARCHAR(20)) AND t.classif = 3 ' || vWhere || ' ORDER BY ' || vSortOrder || ') - ' || vQuery || vExcept + ' || vQuery || vExcept || ' ORDER BY ' || vSortOrderExcept LOOP RETURN NEXT vRecord; END LOOP; diff --git a/SAS/OTDB_Services/CMakeLists.txt b/SAS/OTDB_Services/CMakeLists.txt index 8a4d083500176779af0fe4d980e8ed325f66f887..2174576698b191a85fbf8a746bfc3cad8a9063bb 100644 --- a/SAS/OTDB_Services/CMakeLists.txt +++ b/SAS/OTDB_Services/CMakeLists.txt @@ -6,12 +6,15 @@ lofar_find_package(Python 2.6 REQUIRED) include(PythonInstall) lofar_add_bin_scripts( + getOTDBParset + setOTDBTreeStatus TreeService.py TreeStatusEvents.py ) set(_py_files config.py + otdbrpc.py OTDBBusListener.py ) diff --git a/SAS/OTDB_Services/OTDBBusListener.py b/SAS/OTDB_Services/OTDBBusListener.py index d699cf878a5119471812c357f12396c1e25a3bcb..65b740a38e470031a367216e774d54981cf784a1 100644 --- a/SAS/OTDB_Services/OTDBBusListener.py +++ b/SAS/OTDB_Services/OTDBBusListener.py @@ -93,6 +93,8 @@ class OTDBBusListener(AbstractBusListener): self.onObservationFinished(treeId, modificationTime) elif msg.content['state'] == 'aborted': self.onObservationAborted(treeId, modificationTime) + elif msg.content['state'] == 'obsolete': + self.onObservationObsolete(treeId, modificationTime) def onObservationDescribed(self, treeId, modificationTime): pass @@ -130,4 +132,7 @@ class OTDBBusListener(AbstractBusListener): def onObservationAborted(self, treeId, modificationTime): pass + def onObservationObsolete(self, treeId, modificationTime): + pass + __all__ = ["OTDBBusListener"] diff --git a/SAS/OTDB_Services/TreeService.ini b/SAS/OTDB_Services/TreeService.ini index 090d256417c78ebe489d46417657739f082dbbb5..2499920a0db59d4fdd68738bc7a6ffa0950a4572 100644 --- a/SAS/OTDB_Services/TreeService.ini +++ b/SAS/OTDB_Services/TreeService.ini @@ -1,5 +1,5 @@ [program:TreeService] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;TreeService.py --dbcredentials=OTDB' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec TreeService.py --dbcredentials=OTDB' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/OTDB_Services/TreeService.py b/SAS/OTDB_Services/TreeService.py index 12e6793bf8194424dead20c7728f1fe8c19a191f..edc84de6e8dc84ec72f2980f5d661184b57e6cb6 100755 --- a/SAS/OTDB_Services/TreeService.py +++ b/SAS/OTDB_Services/TreeService.py @@ -41,6 +41,7 @@ SetProject --- : Creates or updates the information of a project import sys, time, pg import logging +from datetime import datetime from lofar.messaging.Service import * from lofar.common.util import waitForInterrupt @@ -92,7 +93,7 @@ def TaskGetIDs(input_dict, db_connection, return_tuple=True): if otdb_id is not None: try: (real_otdb_id, real_mom_id,tree_type) =\ - db_connection.query("select treeid,momid,type from getTreeInfo({}, False)".format(otdb_id)).getresult()[0] + db_connection.query("select treeid,momid,type from getTreeInfo({0}, False)".format(otdb_id)).getresult()[0] return (tree_type, real_otdb_id, real_mom_id) if return_tuple else [tree_type, real_otdb_id, real_mom_id] except QUERY_EXCEPTIONS: pass @@ -101,14 +102,14 @@ def TaskGetIDs(input_dict, db_connection, return_tuple=True): if mom_id is not None: try: (real_otdb_id, real_mom_id,tree_type) =\ - db_connection.query("select treeid,momid,type from getTreeInfo({}, True)".format(mom_id)).getresult()[0] + db_connection.query("select treeid,momid,type from getTreeInfo({0}, True)".format(mom_id)).getresult()[0] return (tree_type, real_otdb_id, real_mom_id) if return_tuple else [tree_type, real_otdb_id, real_mom_id] except QUERY_EXCEPTIONS: pass # Task not found in any way return input values to the user return (None, otdb_id, mom_id) if return_tuple else [None, otdb_id, mom_id] - + # TODO have this return a Dict # Task Get Specification def TaskGetSpecification(input_dict, db_connection): @@ -128,7 +129,7 @@ def TaskGetSpecification(input_dict, db_connection): # if task i not found it is end of story. if task_type is None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) # Try to get the specification information try: @@ -157,6 +158,25 @@ def TaskGetSpecification(input_dict, db_connection): answer_dict["tree"] = answer_list return {'TaskSpecification':answer_dict} +# Task Get TreeInfo +def TaskGetTreeInfo(otdb_id, db_connection): + result = db_connection.query("""select momID, groupID, d_creation, modificationdate, state, starttime, stoptime, processType, processSubtype, description + from OTDBtree where treeID = %s""" % (otdb_id,)).getresult() + + if result: + result = list(result[0]) + for i in [2, 3, 5, 6]: + if '.' in result[i]: + result[i] = datetime.strptime(result[i], '%Y-%m-%d %H:%M:%S.%f') + else: + result[i] = datetime.strptime(result[i], '%Y-%m-%d %H:%M:%S') + + return {'OtdbID':otdb_id, 'momID': result[0], 'groupID': result[1], 'creationtime': result[2], 'modificationtime': result[3], + 'state': result[4], 'starttime': result[5], 'stoptime': result[6], + 'processType': result[7], 'processSubtype': result[8], 'description': result[9]} + + return None + # Task Create def TaskCreate(input_dict, db_connection): @@ -193,7 +213,7 @@ def TaskCreate(input_dict, db_connection): # when otdb_id = None task is not in the database # if we searched on OtdbID and the task is not found then is it end-of-story if task_type is None and otdb_id is not None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) # if we searched on MomID and the task is not found that we try to create a task(template) if task_type is None and mom_id is not None: @@ -205,19 +225,19 @@ def TaskCreate(input_dict, db_connection): # template_info is a list with tuples: (treeid,name) all_names = [ name for (_,name) in template_info if name[0] != '#' ] if not selected_template in all_names: - raise AttributeError("DefaultTemplate '{}' not found, available are:{}".format(selected_template, all_names)) + raise AttributeError("DefaultTemplate '{0}' not found, available are:{1}".format(selected_template, all_names)) # Yeah, default template exist, now make a copy of it. template_ids = [ tasknr for (tasknr,name) in template_info if name == selected_template ] if len(template_ids) != 1: - raise FunctionError("Programming error: matching task_ids for template {} are {}".\ + raise FunctionError("Programming error: matching task_ids for template {0} are {1}".\ format(selected_template, template_ids)) - otdb_id = db_connection.query("select copyTree(1,{})".format(template_ids[0])).getresult()[0][0] + otdb_id = db_connection.query("select copyTree(1,{0})".format(template_ids[0])).getresult()[0][0] # give new tree the mom_id when mom_id was specified by the user. campaign_name = input_dict.get('CampaignName','no campaign') if mom_id is None: mom_id = 0 - db_connection.query("select setMomInfo(1,{},{},0,'{}')".format(otdb_id, mom_id, campaign_name)) + db_connection.query("select setMomInfo(1,{0},{1},0,'{2}')".format(otdb_id, mom_id, campaign_name)) except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("Error while create task from template {}: {}".format(selected_template, exc_info)) + raise FunctionError("Error while create task from template {0}: {1}".format(selected_template, exc_info)) # When we are here we always have a task, so do the key updates return TaskSetSpecification({'OtdbID':otdb_id, 'Specification':input_dict['Specification']}, db_connection) @@ -256,7 +276,7 @@ def TaskSetStatus(input_dict, db_connection): # if task i not found it is end of story. if task_type is None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) try: new_status = input_dict['NewStatus'] @@ -307,9 +327,9 @@ def TaskSetSpecification(input_dict, db_connection): # if task i not found it is end of story. if task_type is None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) if task_type == HARDWARE_TREE: - raise FunctionError("OtdbID/MomID {}/{} refers to a hardware tree.".format(otdb_id, mom_id)) + raise FunctionError("OtdbID/MomID {0}/{1} refers to a hardware tree.".format(otdb_id, mom_id)) try: update_list = input_dict['Specification'] @@ -324,12 +344,12 @@ def TaskSetSpecification(input_dict, db_connection): for (key, value) in update_list.iteritems(): try: if task_type == TEMPLATE_TREE: - (node_id,name) = db_connection.query("select nodeid,name from getVTitem({},'{}')"\ + (node_id,name) = db_connection.query("select nodeid,name from getVTitem({0},'{1}')"\ .format(otdb_id, key)).getresult()[0] - record_list = db_connection.query("select nodeid,instances,limits from getVTitemlist ({},'{}') where nodeid={}"\ + record_list = db_connection.query("select nodeid,instances,limits from getVTitemlist ({0},'{1}') where nodeid={2}"\ .format(otdb_id, name, node_id)).getresult() else: # VIC_TREE - record_list = db_connection.query("select nodeid,instances,limits from getVHitemlist ({},'{}')"\ + record_list = db_connection.query("select nodeid,instances,limits from getVHitemlist ({0},'{1}')"\ .format(otdb_id, key)).getresult() if len(record_list) == 0: errors[key] = "Not found for tree %d" % otdb_id @@ -375,16 +395,16 @@ def TaskPrepareForScheduling(input_dict, db_connection): # if task i not found it is end of story. if task_type is None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) if task_type == HARDWARE_TREE: - raise FunctionError("OtdbID/MomID {}/{} refers to a hardware tree.".format(otdb_id, mom_id)) + raise FunctionError("OtdbID/MomID {0}/{1} refers to a hardware tree.".format(otdb_id, mom_id)) # get the information of the task try: - (task_id,task_type,task_state) = db_connection.query("select treeid,type,state from getTreeInfo({},False)"\ + (task_id,task_type,task_state) = db_connection.query("select treeid,type,state from getTreeInfo({0},False)"\ .format(otdb_id)).getresult()[0] except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("TaskPrepareForScheduling: {}".format(exc_info)) + raise FunctionError("TaskPrepareForScheduling: {0}".format(exc_info)) # Get list of defines tree states state_names = {} @@ -394,26 +414,26 @@ def TaskPrepareForScheduling(input_dict, db_connection): state_names[name] = nr state_nrs[nr] = name except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("Error while getting list of task states for tree {}: {}".format(otdb_id, exc_info)) + raise FunctionError("Error while getting list of task states for tree {0}: {1}".format(otdb_id, exc_info)) # If task is of the type VItemplate convert it to a VHtree delete_old_task = False if task_type == TEMPLATE_TREE: try: # create executable task - new_task_id = db_connection.query("select instanciateVHtree(1,{})".format(task_id)).getresult()[0][0] + new_task_id = db_connection.query("select instanciateVHtree(1,{0})".format(task_id)).getresult()[0][0] # get the characteristics - (task_id,task_type,task_state) = db_connection.query("select treeid,type,state from getTreeInfo({},False)"\ + (task_id,task_type,task_state) = db_connection.query("select treeid,type,state from getTreeInfo({0},False)"\ .format(new_task_id)).getresult()[0] delete_old_task = True except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("TaskPrepareForScheduling: failed for task {}: {}".format(otdb_id, exc_info)) + raise FunctionError("TaskPrepareForScheduling: failed for task {0}: {1}".format(otdb_id, exc_info)) # make sure the tree is in the right state if task_state != state_names['approved']: try: - db_connection.query("select setTreeState(1,{},{}::INT2,True)".format(task_id, state_names['approved'])) + db_connection.query("select setTreeState(1,{0},{1}::INT2,True)".format(task_id, state_names['approved'])) except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("Error while setting task {} to 'approved': {}".format(task_id, exc_info)) + raise FunctionError("Error while setting task {0} to 'approved': {1}".format(task_id, exc_info)) if delete_old_task: TaskDelete({'OtdbID':otdb_id}, db_connection) @@ -423,9 +443,9 @@ def TaskPrepareForScheduling(input_dict, db_connection): end_time = input_dict.get("StopTime", "") if start_time != "" or end_time != "": try: - db_connection.query("select setSchedule(1,{},'{}','{}')".format(task_id,start_time,end_time)) + db_connection.query("select setSchedule(1,{0},'{1}','{2}')".format(task_id,start_time,end_time)) except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("Error while setting schedule-times of task {} to '{}'-'{}': {}"\ + raise FunctionError("Error while setting schedule-times of task {0} to '{1}'-'{2}': {3}"\ .format(task_id, start_time, end_time, exc_info)) return {'OtdbID':task_id, 'MomID':mom_id, 'Success':True} @@ -449,13 +469,13 @@ def TaskDelete(input_dict, db_connection): # if task i not found it is end of story. if task_type is None: - raise FunctionError("Task with OtdbID/MomID {}/{} does not exist".format(otdb_id, mom_id)) + raise FunctionError("Task with OtdbID/MomID {0}/{1} does not exist".format(otdb_id, mom_id)) # delete the task try: - db_connection.query("select deleteTree(1,{})".format(otdb_id)) + db_connection.query("select deleteTree(1,{0})".format(otdb_id)) except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("TaskDelete {}: {}".format(otdb_id, exc_info)) + raise FunctionError("TaskDelete {0}: {1}".format(otdb_id, exc_info)) return {'OtdbID':otdb_id, 'MomID':mom_id, 'Success':True} @@ -478,7 +498,7 @@ def GetDefaultTemplates(input_dict, db_connection): if name[0] != '#': Templates[name] = { 'OtdbID':treeid, 'processType':proc_type, 'processSubtype':proc_subtype, 'Strategy':strategy} except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("GetDefaulTemplates: {}".format(exc_info)) + raise FunctionError("GetDefaulTemplates: {0}".format(exc_info)) return { 'DefaultTemplates': Templates } @@ -502,7 +522,7 @@ def GetStations(input_dict, db_connection): if len(level) == 4: Stations[level[3]] = level[2] except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("GetStations: {}".format(exc_info)) + raise FunctionError("GetStations: {0}".format(exc_info)) return { 'Stations': Stations } @@ -529,14 +549,14 @@ def SetProject(input_dict, db_connection): contact = input_dict['contact'] except KeyError, info: raise AttributeError("SetProject: Key %s is missing in the input" % info) - logger.info("SetProject for project: {}".format(project_name)) + logger.info("SetProject for project: {0}".format(project_name)) # get the information Stations = {} try: - project_id = db_connection.query("select saveCampaign(0,'{}','{}','{}','{}','{}')".format(project_name, title, pi, co_i, contact)).getresult()[0][0] + project_id = db_connection.query("select saveCampaign(0,'{0}','{1}','{2}','{3}','{4}')".format(project_name, title, pi, co_i, contact)).getresult()[0][0] except QUERY_EXCEPTIONS, exc_info: - raise FunctionError("SetProject: {}".format(exc_info)) + raise FunctionError("SetProject: {0}".format(exc_info)) return { "projectID": project_id } @@ -569,7 +589,8 @@ class PostgressMessageHandler(MessageHandlerInterface): "TaskDelete": self._TaskDelete, "GetDefaultTemplates": self._GetDefaultTemplates, "GetStations": self._GetStations, - "SetProject": self._SetProject + "SetProject": self._SetProject, + "TaskGetTreeInfo": self._TaskGetTreeInfo } def prepare_receive(self): @@ -588,35 +609,39 @@ class PostgressMessageHandler(MessageHandlerInterface): # The following functions are called from the Service code. def _TaskGetSpecification(self, **kwargs): - logger.info("_TaskGetSpecification({})".format(kwargs)) + logger.info("_TaskGetSpecification({0})".format(kwargs)) return TaskGetSpecification(kwargs, self.connection) def _TaskCreate(self, **kwargs): - logger.info("_TaskCreate({})".format(kwargs)) + logger.info("_TaskCreate({0})".format(kwargs)) return TaskCreate(kwargs, self.connection) def _TaskGetStatus(self, **kwargs): - logger.info("_TaskGetStatus({})".format(kwargs)) + logger.info("_TaskGetStatus({0})".format(kwargs)) return TaskGetStatus(kwargs.get('otdb_id'), self.connection) def _TaskSetStatus(self, **kwargs): - logger.info("_TaskSetStatus({})".format(kwargs)) + logger.info("_TaskSetStatus({0})".format(kwargs)) return TaskSetStatus(kwargs, self.connection) + def _TaskGetTreeInfo(self, **kwargs): + logger.info("_TaskGetTreeInfo({0})".format(kwargs)) + return TaskGetTreeInfo(kwargs.get('otdb_id'), self.connection) + def _TaskSetSpecification(self, **kwargs): - logger.info("_TaskSetSpecification({})".format(kwargs)) + logger.info("_TaskSetSpecification({0})".format(kwargs)) return TaskSetSpecification(kwargs, self.connection) def _TaskPrepareForScheduling(self, **kwargs): - logger.info("_TaskPrepareForScheduling({})".format(kwargs)) + logger.info("_TaskPrepareForScheduling({0})".format(kwargs)) return TaskPrepareForScheduling(kwargs, self.connection) def _TaskGetIDs(self, **kwargs): - logger.info("_TaskGetIDs({})".format(kwargs)) + logger.info("_TaskGetIDs({0})".format(kwargs)) return TaskGetIDs(kwargs, self.connection, return_tuple=False) def _TaskDelete(self, **kwargs): - logger.info("_TaskDelete({})".format(kwargs)) + logger.info("_TaskDelete({0})".format(kwargs)) return TaskDelete(kwargs, self.connection) def _GetDefaultTemplates(self, **kwargs): @@ -628,7 +653,7 @@ class PostgressMessageHandler(MessageHandlerInterface): return GetStations(kwargs, self.connection) def _SetProject(self, **kwargs): - logger.info("_SetProject({})".format(kwargs)) + logger.info("_SetProject({0})".format(kwargs)) return SetProject(kwargs, self.connection) @@ -641,6 +666,9 @@ if __name__ == "__main__": # Check the invocation arguments parser = OptionParser("%prog [options]") + parser.add_option('-q', '--broker', dest='broker', type='string', + default=None, + help='Address of the qpid broker, default: localhost') parser.add_option("-B", "--busname", dest="busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, help="Busname or queue-name on which RPC commands are received. [default: %default") @@ -650,6 +678,7 @@ if __name__ == "__main__": parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') # Add options of dbcredentials: --database, --host, ... parser.add_option_group(dbcredentials.options_group(parser)) + parser.set_defaults(dbcredentials="OTDB") (options, args) = parser.parse_args() setQpidLogLevel(logging.INFO) @@ -662,6 +691,7 @@ if __name__ == "__main__": use_service_methods=True, numthreads=1, handler_args={"dbcreds" : dbcreds}, + broker=options.broker, verbose=True): waitForInterrupt() diff --git a/SAS/OTDB_Services/TreeStatusEvents.ini b/SAS/OTDB_Services/TreeStatusEvents.ini index b16300df8c78169bd92f28e73e1984172edeba34..f2458fa5437548c3621187c153b68f27c4cbf38c 100644 --- a/SAS/OTDB_Services/TreeStatusEvents.ini +++ b/SAS/OTDB_Services/TreeStatusEvents.ini @@ -1,5 +1,5 @@ [program:TreeStatusEvents] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;TreeStatusEvents.py --dbcredentials=OTDB' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec TreeStatusEvents.py --dbcredentials=OTDB' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/OTDB_Services/TreeStatusEvents.py b/SAS/OTDB_Services/TreeStatusEvents.py index 1b896e6048cae512446d5e61ad2a3b63d2c30ad8..636dc4b51165f63d57480fb709580f5d16e978a1 100755 --- a/SAS/OTDB_Services/TreeStatusEvents.py +++ b/SAS/OTDB_Services/TreeStatusEvents.py @@ -136,7 +136,7 @@ if __name__ == "__main__": try: logger.info("creating %s" % (treestatuseventfilename,)) if not os.path.exists(os.path.dirname(treestatuseventfilename)): - os.mkdirs(os.path.dirname(treestatuseventfilename)) + os.makedirs(os.path.dirname(treestatuseventfilename)) with open(treestatuseventfilename, 'w') as f: f.write(start_time.strftime("%Y-%m-%d %H:%M:%S")) diff --git a/SAS/OTDB_Services/getOTDBParset b/SAS/OTDB_Services/getOTDBParset new file mode 100755 index 0000000000000000000000000000000000000000..138475004ee649818bb79dbf02b79c7e13c71d45 --- /dev/null +++ b/SAS/OTDB_Services/getOTDBParset @@ -0,0 +1,56 @@ +#!/usr/bin/env python +#coding: iso-8859-15 +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id$ +""" + +""" + +from lofar.messaging.RPC import RPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME +from lofar.sas.otdb.otdbrpc import OTDBRPC + +if __name__ == "__main__": + from optparse import OptionParser + import logging + import sys + + logging.basicConfig(stream=sys.stdout, level=logging.INFO) + logger = logging.getLogger(__name__) + + # Check the invocation arguments + parser = OptionParser("%prog -o obsid [options]") + parser.add_option("-B", "--busname", dest="busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, + help="Busname on which OTDB commands are sent") + parser.add_option("-o", "--obsid", dest="obsid", type="int", default=0, + help="Observation/tree ID to get parset of") + (options, args) = parser.parse_args() + + if not options.busname or not options.obsid: + parser.print_help() + sys.exit(1) + + with OTDBRPC(busname=options.busname) as otdbrpc: + parset = otdbrpc.taskGetSpecification(otdb_id=options.obsid)["specification"] + + for k,v in parset.iteritems(): + print "%s = %s" % (k,v) + diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/otdbrpc.py b/SAS/OTDB_Services/otdbrpc.py similarity index 94% rename from SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/otdbrpc.py rename to SAS/OTDB_Services/otdbrpc.py index 8c71aea1cf4bfe0697367d9e3156b07ea27e382e..a7d60c8d4a48a4d9e35ef1dcf392e785a80fef97 100644 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/otdbrpc.py +++ b/SAS/OTDB_Services/otdbrpc.py @@ -20,8 +20,9 @@ class OTDBPRCException(Exception): class OTDBRPC(RPCWrapper): def __init__(self, busname=DEFAULT_OTDB_SERVICE_BUSNAME, servicename=DEFAULT_OTDB_SERVICENAME, - broker=None): - super(OTDBRPC, self).__init__(busname, servicename, broker) + broker=None, + timeout=120): + super(OTDBRPC, self).__init__(busname, servicename, broker, timeout=timeout) def taskGetIDs(self, otdb_id=None, mom_id=None): if otdb_id: @@ -57,6 +58,12 @@ class OTDBRPC(RPCWrapper): raise OTDBPRCException("TaskCreate failed for MoM ID %i" % (mom_id,)) return {"mom_id": answer["MomID"], "otdb_id": answer["OtdbID"]} + def taskGetTreeInfo(self, otdb_id): + info = self.rpc('TaskGetTreeInfo', otdb_id=otdb_id) + for key in ['creationtime', 'modificationtime', 'starttime', 'stoptime']: + info[key] = info[key].datetime() + return info + def taskGetStatus(self, otdb_id): return self.rpc('TaskGetStatus', otdb_id=otdb_id)['status'] diff --git a/SAS/OTDB_Services/setOTDBTreeStatus b/SAS/OTDB_Services/setOTDBTreeStatus new file mode 100755 index 0000000000000000000000000000000000000000..06625da83657e56c18e0a709f3662466cec8330c --- /dev/null +++ b/SAS/OTDB_Services/setOTDBTreeStatus @@ -0,0 +1,55 @@ +#!/usr/bin/env python +#coding: iso-8859-15 +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id$ +""" + +""" + +from lofar.messaging.RPC import RPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME +from lofar.sas.otdb.otdbrpc import OTDBRPC + +if __name__ == "__main__": + from optparse import OptionParser + import logging + import sys + + logging.basicConfig(stream=sys.stdout, level=logging.INFO) + logger = logging.getLogger(__name__) + + # Check the invocation arguments + parser = OptionParser("%prog -o obsid -s status [options]") + parser.add_option("-B", "--busname", dest="busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, + help="Busname on which OTDB commands are sent") + parser.add_option("-o", "--obsid", dest="obsid", type="int", default=0, + help="Observation/tree ID to set status for") + parser.add_option("-s", "--status", dest="status", type="string", default="", + help="New status") + (options, args) = parser.parse_args() + + if not options.busname or not options.obsid or not options.status: + parser.print_help() + sys.exit(1) + + with OTDBRPC(busname=options.busname) as otdbrpc: + otdbrpc.taskSetStatus(otdb_id=options.obsid, new_status=options.status) + diff --git a/SAS/OTDB_Services/test/t_TreeService.py b/SAS/OTDB_Services/test/t_TreeService.py index d1f7bf17cbf273faf9232526f10be240a57c790f..afae920a6e16c2981b7b50d04391ba4f74fee885 100644 --- a/SAS/OTDB_Services/test/t_TreeService.py +++ b/SAS/OTDB_Services/test/t_TreeService.py @@ -37,18 +37,18 @@ logger = logging.getLogger(__name__) def do_rpc_catch_exception(exc_text, rpc_instance, arg_dict): try: - print "** Executing {}({})...".format(rpc_instance.ServiceName,arg_dict) + print "** Executing {0}({1})...".format(rpc_instance.ServiceName,arg_dict) (data, status) = (rpc_instance)(**arg_dict) - raise Exception("Expected an exception {}, didn't get any".format(exc_text)) + raise Exception("Expected an exception {0}, didn't get any".format(exc_text)) except Exception: - print "Caught expected exception {}".format(exc_text) + print "Caught expected exception {0}".format(exc_text) print "======" def do_rpc(rpc_instance, arg_dict): - print "** Executing {}({})...".format(rpc_instance.ServiceName,arg_dict) + print "** Executing {0}({1})...".format(rpc_instance.ServiceName,arg_dict) (data, status) = (rpc_instance)(**arg_dict) if status != "OK": - raise Exception("Status returned is {}".format(status)) + raise Exception("Status returned is {0}".format(status)) # if isinstance(data, dict): # for key in sorted(data): # print "%s ==> %s" % (key, data[key]) diff --git a/SAS/OTDB_Services/test/t_TreeService.run b/SAS/OTDB_Services/test/t_TreeService.run index 6a7e1fb69044905b679fc833d0b47a769216553d..5d1c193ccb2a7689528856d11fca86aa2b49faba 100755 --- a/SAS/OTDB_Services/test/t_TreeService.run +++ b/SAS/OTDB_Services/test/t_TreeService.run @@ -3,19 +3,20 @@ DBHOST=10.149.32.21 # sas099.control.lofar #cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM -trap 'qpid-config del exchange --force $queue ; kill ${SERVER_PID}' 0 1 2 3 15 +trap 'qpid-config del exchange --force $queue ; kill ${SERVICE_PID} ; dropdb -U postgres -h ${DBHOST} ${DBNAME}' 0 1 2 3 15 # Generate randome queue name -queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) +queue=$(< /dev/urandom tr -dc [:alnum:] | head -c10) +DBNAME=unittest_$queue # Create the queue qpid-config add exchange topic $queue # Setup a clean database with predefined content -dropdb -U postgres -h ${DBHOST} unittest_db -gzip -dc $srcdir/unittest_db.dump.gz | psql -U postgres -h ${DBHOST} -f - -TreeService.py -B $queue -D unittest_db -H ${DBHOST} -U postgres & -SERVER_PID=$! +createdb -U postgres -h ${DBHOST} ${DBNAME} +gzip -dc $srcdir/unittest_db.dump.gz | psql -U postgres -h ${DBHOST} ${DBNAME} -f - +TreeService.py -B $queue -D ${DBNAME} -H ${DBHOST} -U postgres & +SERVICE_PID=$! # Starting up takes a while sleep 3 diff --git a/SAS/OTDB_Services/test/t_TreeStatusEvents.run b/SAS/OTDB_Services/test/t_TreeStatusEvents.run index 4a2d5718f825f4f4d3043c26ce3dd8fe06620d97..a1f81976a9787725a257a1eaad738e9dd37b5646 100755 --- a/SAS/OTDB_Services/test/t_TreeStatusEvents.run +++ b/SAS/OTDB_Services/test/t_TreeStatusEvents.run @@ -3,22 +3,23 @@ DBHOST=sas099.control.lofar #cleanup on normal exit and on SIGHUP, SIGINT, SIGQUIT, and SIGTERM -trap 'qpid-config del exchange --force $queue ; kill ${SERVICE_PID}' 0 1 2 3 15 +trap 'qpid-config del exchange --force $queue ; kill ${SERVICE_PID} ; dropdb -U postgres -h ${DBHOST} ${DBNAME}' 0 1 2 3 15 # Generate randome queue name -queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16) +queue=$(< /dev/urandom tr -dc [:alnum:] | head -c10) +DBNAME=unittest_${queue} # Create the queue qpid-config add exchange topic $queue # Setup a clean database with predefined content -dropdb -U postgres -h ${DBHOST} unittest_db -gzip -dc $srcdir/unittest_db.dump.gz | psql -U postgres -h ${DBHOST} -f - -TreeStatusEvents.py -B $queue -D unittest_db -H ${DBHOST} -U postgres & +createdb -U postgres -h ${DBHOST} ${DBNAME} +gzip -dc $srcdir/unittest_db.dump.gz | psql -U postgres -h ${DBHOST} ${DBNAME} -f - +TreeStatusEvents.py -B $queue -D ${DBNAME} -H ${DBHOST} -U postgres & SERVICE_PID=$! # Starting up takes a while sleep 3 # Run the unit test source python-coverage.sh -python_coverage_test "Messaging/python" t_TreeStatusEvents.py -D unittest_db -H ${DBHOST} -B $queue +python_coverage_test "Messaging/python" t_TreeStatusEvents.py -D ${DBNAME} -H ${DBHOST} -B $queue diff --git a/SAS/OTDB_Services/test/unittest_db.dump.gz b/SAS/OTDB_Services/test/unittest_db.dump.gz index 3f42fb231b6df3789e2b904cc13c484ba23a74e4..961fb48516052918b0eeb2ee562216f24662d2de 100644 Binary files a/SAS/OTDB_Services/test/unittest_db.dump.gz and b/SAS/OTDB_Services/test/unittest_db.dump.gz differ diff --git a/SAS/QPIDInfrastructure/bin/addtoQPIDDB.py b/SAS/QPIDInfrastructure/bin/addtoQPIDDB.py index 0d12ef72902e485b4e1eb508412dc1323d95edc7..de5793a340492284d3cc9de0c54014c3f6aed1c6 100755 --- a/SAS/QPIDInfrastructure/bin/addtoQPIDDB.py +++ b/SAS/QPIDInfrastructure/bin/addtoQPIDDB.py @@ -14,6 +14,7 @@ if __name__ == '__main__': parser.add_option('-q', '--queue', dest='queue', type='string', default=None, help='Name of the queue on the broker') parser.add_option('-e', '--exchange', dest='exchange', type='string', default=None, help='Name of the exchange on the broker') parser.add_option('-k', '--routingkey', dest='routingkey', type='string', default='#', help='Federation routing key') + parser.add_option('-d', '--dynamic', dest='dynamic', action="store_true", default=False, help='Create a dynamic route') parser.add_option_group(dbcredentials.options_group(parser, "qpidinfra")) (options, args) = parser.parse_args() @@ -55,7 +56,7 @@ if __name__ == '__main__': if (options.exchange): QPIDinfra.addexchange(options.exchange) # should be superfluous QPIDinfra.bindexchangetohost(options.exchange,options.federation) - QPIDinfra.setexchangeroute(options.exchange,options.routingkey,options.broker,options.federation) + QPIDinfra.setexchangeroute(options.exchange,options.routingkey,options.broker,options.federation,dynamic=options.dynamic) else: raise Exception("federation can only be setup with a queue or an exchange") diff --git a/SAS/QPIDInfrastructure/bin/configQPIDfromDB.py b/SAS/QPIDInfrastructure/bin/configQPIDfromDB.py index 38d1ffbc8d9bf7b6e6e236470295491ea2446992..6faa807dd8a2ba1be29d1f55b90d4ce3cac80cae 100755 --- a/SAS/QPIDInfrastructure/bin/configQPIDfromDB.py +++ b/SAS/QPIDInfrastructure/bin/configQPIDfromDB.py @@ -10,8 +10,15 @@ def qpidconfig_add_topic(settings): print ("qpid-config -b %s add exchange topic %s --durable" %(settings['hostname'],settings['exchangename'])) def qpidroute_add(settings): + cmd = "dynamic" if settings['dynamic'] else "route" + print ("qpid-route -d route del %s %s %s \'%s\' " %(settings['tohost'],settings['fromhost'],settings['exchangename'],settings['routingkey'])) - print ("qpid-route -d route add %s %s %s \'%s\' " %(settings['tohost'],settings['fromhost'],settings['exchangename'],settings['routingkey'])) + print ("qpid-route -d dynamic del %s %s %s" %(settings['tohost'],settings['fromhost'],settings['exchangename'])) + + if settings['dynamic']: + print ("qpid-route -d dynamic add %s %s %s" %(settings['tohost'],settings['fromhost'],settings['exchangename'])) + else: + print ("qpid-route -d route add %s %s %s \'%s\' " %(settings['tohost'],settings['fromhost'],settings['exchangename'],settings['routingkey'])) def qpidQroute_add(settings): print ("qpid-route -d queue del %s %s '%s' '%s'" %(settings['tohost'],settings['fromhost'],settings['exchangename'],settings['queuename'])) diff --git a/SAS/QPIDInfrastructure/bin/populateDB.sh b/SAS/QPIDInfrastructure/bin/populateDB.sh index e24f0454a77b5f85956535909f54670c0fb53547..dea9d87798d4382fc4393f760d6e2b8533f00597 100755 --- a/SAS/QPIDInfrastructure/bin/populateDB.sh +++ b/SAS/QPIDInfrastructure/bin/populateDB.sh @@ -1,4 +1,10 @@ #!/bin/bash +# +# To DB first, wipe, use +# +# psql -U postgres -h sas099 --dbname=qpidinfra --file=$LOFARROOT/share/qpidinfrastructure/sql/qpidinfradb.sql +# + # ----------------------------------------- # Configuration @@ -31,10 +37,10 @@ if $PROD; then SCU=scu001.control.lofar SAS=sas001.control.lofar - MOM_USER=lcs023.control.lofar + MOM_SYSTEM=lcs023.control.lofar MOM_INGEST=lcs029.control.lofar - COBALT="`seq -f cbt%03.0f.control.lofar 1 8`" + COBALT="`seq -f cbm%03.0f.control.lofar 1 8`" CEP2="`seq -f locus%03.0f.cep2.lofar 1 94`" CEP2HEAD=lhn001.cep2.lofar @@ -47,10 +53,10 @@ else SCU=scu099.control.lofar SAS=sas099.control.lofar - MOM_USER=lcs028.control.lofar + MOM_SYSTEM=lcs028.control.lofar MOM_INGEST=lcs028.control.lofar - COBALT="cbt009.control.lofar" + COBALT="cbm009.control.lofar" CEP2="locus098.cep2.lofar locus099.cep2.lofar" CEP2HEAD=locus102.cep2.lofar @@ -60,7 +66,19 @@ else fi # ----------------------------------------- -# Cobalt & Pipelines -> MessageRouter +# Cobalt GPUProc -> MessageRouter +# ----------------------------------------- + +for fnode in $COBALT +do + addtoQPIDDB.py --broker $fnode --queue ${PREFIX}lofar.task.feedback.dataproducts --federation $CCU + addtoQPIDDB.py --broker $fnode --queue ${PREFIX}lofar.task.feedback.processing --federation $CCU + addtoQPIDDB.py --broker $fnode --queue ${PREFIX}lofar.task.feedback.state --federation $CCU +done + + +# ----------------------------------------- +# Cobalt OutputProc & Pipelines -> MessageRouter # ----------------------------------------- for tnode in $CEP4HEAD @@ -99,9 +117,9 @@ done # MessageRouter -> MoM # ----------------------------------------- -addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.dataproducts --federation $MOM_USER -addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.processing --federation $MOM_USER -addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.state --federation $MOM_USER +addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.dataproducts --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.processing --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.feedback.state --federation $MOM_SYSTEM # ----------------------------------------- # MessageRouter -> OTDB @@ -121,21 +139,31 @@ addtoQPIDDB.py --broker $CCU --exchange ${PREFIX}mac.task.feedback.state # ----------------------------------------- addtoQPIDDB.py --broker $MCU --queue ${PREFIX}lofar.task.specification.system --federation $CCU -addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.specification.system --federation $MOM_USER +addtoQPIDDB.py --broker $CCU --queue ${PREFIX}mom.task.specification.system --federation $MOM_SYSTEM # ----------------------------------------- # MoM <-> MoM-OTDB-Adapter # ----------------------------------------- -addtoQPIDDB.py --broker $SAS --queue mom.command --federation $MOM_USER -addtoQPIDDB.py --broker $SAS --queue mom.importxml --federation $MOM_USER -addtoQPIDDB.py --broker $MOM_USER --queue mom-otdb-adapter.importxml --federation $SAS +addtoQPIDDB.py --broker $SAS --queue mom.command --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $SAS --queue mom.importxml --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $MOM_SYSTEM --queue mom-otdb-adapter.importxml --federation $SAS # ----------------------------------------- # MoM Services # ----------------------------------------- -addtoQPIDDB.py --broker $MOM_USER --exchange lofar.mom.bus -addtoQPIDDB.py --broker $MOM_INGEST --exchange lofar.mom.bus +addtoQPIDDB.py --broker $MOM_SYSTEM --exchange ${PREFIX}lofar.mom.bus +addtoQPIDDB.py --broker $MOM_INGEST --exchange ${PREFIX}lofar.mom.bus +addtoQPIDDB.py --broker $MOM_SYSTEM --exchange ${PREFIX}lofar.mom.command +addtoQPIDDB.py --broker $MOM_SYSTEM --exchange ${PREFIX}lofar.mom.notification + +# ----------------------------------------- +# MoM Services <-> ResourceAssignment +# ----------------------------------------- + +addtoQPIDDB.py --broker $SCU --exchange ${PREFIX}lofar.mom.bus --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $SCU --exchange ${PREFIX}lofar.mom.command --federation $MOM_SYSTEM +addtoQPIDDB.py --broker $MOM_SYSTEM --exchange ${PREFIX}lofar.mom.notification --federation $SCU # ----------------------------------------- # ResourceAssignment @@ -148,14 +176,15 @@ addtoQPIDDB.py --broker $SCU --exchange ${PREFIX}lofar.otdb.notification addtoQPIDDB.py --broker $SCU --exchange ${PREFIX}lofar.ssdb.command addtoQPIDDB.py --broker $SCU --exchange ${PREFIX}lofar.ssdb.notification -# TODO: messages will end up at $SCU twice? -for tnode in head{01..02}.cep4 +for head in head01.cep4.control.lofar do - for fnode in cpu{01..50}.cep4 + for cpu in $CEP4 do - addtoQPIDDB.py --broker $fnode --exchange ${PREFIX}lofar.otdb.command --federation $tnode + addtoQPIDDB.py --dynamic --broker $cpu --exchange ${PREFIX}lofar.otdb.command --federation $head + addtoQPIDDB.py --dynamic --broker $head --exchange ${PREFIX}lofar.otdb.command --federation $cpu done - addtoQPIDDB.py --broker $tnode --exchange ${PREFIX}lofar.otdb.command --federation $SCU + addtoQPIDDB.py --dynamic --broker $head --exchange ${PREFIX}lofar.otdb.command --federation $SCU + addtoQPIDDB.py --dynamic --broker $SCU --exchange ${PREFIX}lofar.otdb.command --federation $head done diff --git a/SAS/ResourceAssignment/CMakeLists.txt b/SAS/ResourceAssignment/CMakeLists.txt index 92b15a3a3f9bfa5ca2f9a92b7ed7a74465cdb808..a391f0f99f467ee90aaed5d7d010bed5b4e313d7 100644 --- a/SAS/ResourceAssignment/CMakeLists.txt +++ b/SAS/ResourceAssignment/CMakeLists.txt @@ -10,5 +10,6 @@ lofar_add_package(ResourceAssignmentService) lofar_add_package(SystemStatusDatabase) lofar_add_package(SystemStatusService) lofar_add_package(OTDBtoRATaskStatusPropagator) +lofar_add_package(RAScripts) diff --git a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator.ini b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator.ini index be97662836eae4978f3582c8aa527556ba30fb74..fc1b92a4cdb182baecc40f688e07ce94e6d3f6c7 100644 --- a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator.ini +++ b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/otdbtorataskstatuspropagator.ini @@ -1,5 +1,5 @@ [program:ortspropagator] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;otdbtorataskstatuspropagator' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec otdbtorataskstatuspropagator' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py index c0367f03836d79e116c8be015470f14c94c0f2c2..3b14e6046179d8d45912b616789c6fca4fd21b51 100644 --- a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py +++ b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py @@ -5,9 +5,12 @@ TODO: add doc ''' import logging +from datetime import datetime, timedelta from optparse import OptionParser -from lofar.sas.otdb.OTDBBusListener import OTDBBusListener from lofar.common.util import waitForInterrupt +from lofar.sas.otdb.OTDBBusListener import OTDBBusListener +from lofar.sas.otdb.otdbrpc import OTDBRPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_BUSNAME, DEFAULT_OTDB_NOTIFICATION_SUBJECT from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as DEFAULT_RADB_BUSNAME from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RADB_SERVICENAME @@ -19,6 +22,8 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): def __init__(self, otdb_notification_busname=DEFAULT_OTDB_NOTIFICATION_BUSNAME, otdb_notification_subject=DEFAULT_OTDB_NOTIFICATION_SUBJECT, + otdb_service_busname=DEFAULT_OTDB_SERVICE_BUSNAME, + otdb_service_subject=DEFAULT_OTDB_SERVICENAME, radb_busname=DEFAULT_RADB_BUSNAME, radb_servicename=DEFAULT_RADB_SERVICENAME, broker=None, **kwargs): @@ -27,28 +32,72 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): broker=broker, **kwargs) + self.otdb = OTDBRPC(busname=otdb_service_busname, servicename=otdb_service_subject, broker=broker) ## , ForwardExceptions=True hardcoded in RPCWrapper right now self.radb = RARPC(busname=radb_busname, servicename=radb_servicename, broker=broker) def start_listening(self, **kwargs): + self.otdb.open() self.radb.open() super(OTDBtoRATaskStatusPropagator, self).start_listening(**kwargs) def stop_listening(self, **kwargs): + self.otdb.close() self.radb.close() super(OTDBtoRATaskStatusPropagator, self).stop_listening(**kwargs) def _update_radb_task_status(self, otdb_id, task_status): - logger.info("updating radb-task with otdb_id %s to status %s" % (otdb_id, task_status)) - result = self.radb.updateTaskStatusForOtdbId(otdb_id=otdb_id, status=task_status) - - if not result or 'updated' not in result or not result['updated']: - logger.warning("could not update task with otdb_id %s to status %s" % (otdb_id, task_status)) + try: + if task_status in ['approved', 'prescheduled', 'obsolete']: + radb_task = self.radb.getTask(otdb_id=otdb_id) + if (radb_task and + radb_task['cluster'] == 'CEP4' and + radb_task['status'] in ['queued', 'active', 'completing']): + # set task to aborted first, so other controls (e.g. pipelinecontrol) + # can respond to the aborted event + logger.info("aborting radb-task with otdb_id %s from status %s" % (otdb_id, radb_task['status'])) + result = self.radb.updateTaskStatusForOtdbId(otdb_id=otdb_id, status='aborted') + + logger.info("updating radb-task with otdb_id %s to status %s" % (otdb_id, task_status)) + result = self.radb.updateTaskStatusForOtdbId(otdb_id=otdb_id, status=task_status) + + if not result or 'updated' not in result or not result['updated']: + logger.warning("could not update task with otdb_id %s to status %s" % (otdb_id, task_status)) + except Exception as e: + logger.error(e) + + def _updateStartStopTimesFromSpecification(self, treeId): + # cep2 jobs still get scheduled via old scheduler + # so, if the start/endtime were changed in the old scheduler + # and the times are different to the radb times, then propagate these to radb + try: + radb_task = self.radb.getTask(otdb_id=treeId) + if radb_task: + # all CEP4 tasks have storage claim(s) + # and we do not want OTDB to update our RA scheduled tasks + # so check if we have storage claims + claims = self.radb.getResourceClaims(task_ids=radb_task['id'], resource_type='storage') + + if not claims: + #this is a CEP2 task, modified by the old scheduler + #update start/stop time + spec = self.otdb.taskGetSpecification(otdb_id=treeId)['specification'] + new_startime = spec['ObsSW.Observation.startTime'] + new_endtime = spec['ObsSW.Observation.stopTime'] + + new_startime = datetime.strptime(new_startime, ('%Y-%m-%d %H:%M:%S.%f' if '.' in new_startime else '%Y-%m-%d %H:%M:%S')) + new_endtime = datetime.strptime(new_endtime, ('%Y-%m-%d %H:%M:%S.%f' if '.' in new_endtime else '%Y-%m-%d %H:%M:%S')) + + logger.info("Updating task (otdb_id=%s, radb_id=%s, status=%s) startime to \'%s\' and endtime to \'%s\'", treeId, radb_task['id'], radb_task['status'], new_startime, new_endtime) + self.radb.updateTaskAndResourceClaims(radb_task['id'], starttime=new_startime, endtime=new_endtime) + except Exception as e: + logger.error(e) def onObservationPrepared(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'prepared') def onObservationApproved(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'approved') + self._updateStartStopTimesFromSpecification(treeId) def onObservationOnHold(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'on_hold') @@ -56,31 +105,102 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): def onObservationConflict(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'conflict') + def onObservationObsolete(self, treeId, modificationTime): + self._update_radb_task_status(treeId, 'obsolete') + def onObservationPrescheduled(self, treeId, modificationTime): - logger.info("not propagating prescheduled status for otdb_id %s to radb because the resource assigner takes care of this" % (treeId)) + self._update_radb_task_status(treeId, 'prescheduled') def onObservationScheduled(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'scheduled') + self._updateStartStopTimesFromSpecification(treeId) + + try: + radb_task = self.radb.getTask(otdb_id=treeId) + if radb_task: + #reschedule all scheduled successor tasks + #because they might need to update their specification due to this scheduled task + successor_tasks = self.radb.getTasks(task_ids=radb_task['successor_ids'], task_status=['scheduled', 'queued']) + for successor_task in successor_tasks: + logger.info('rescheduling otdb_id=%s because it is a successor of the just scheduled task otdb_id=%s', successor_task['otdb_id'], radb_task['otdb_id']) + self.otdb.taskSetStatus(successor_task['otdb_id'], 'prescheduled') + except Exception as e: + logger.error(e) def onObservationQueued(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'queued') + try: + # pipeline control puts tasks in queued state for pipelines, + # and gives the pipeline to slurm + # from that moment it is not known exactly when the task will run. + # we do know however that it will run after its predecessors, and after 'now' + # reflect that in radb for pipelines + task = self.radb.getTask(otdb_id=treeId) + if task and task['type'] == 'pipeline' and 'predecessor_ids' in task: + predecessor_tasks = [self.radb.getTask(pid) for pid in task['predecessor_ids']] + if predecessor_tasks: + pred_endtimes = [t['endtime'] for t in predecessor_tasks] + max_pred_endtime = max(pred_endtimes) + new_startime = max([max_pred_endtime, datetime.utcnow()]) + new_endtime = new_startime + timedelta(seconds=task['duration']) + + logger.info("Updating task %s (otdb_id=%s, status=queued) startime to \'%s\' and endtime to \'%s\'", task['id'], treeId, new_startime, new_endtime) + self.radb.updateTaskAndResourceClaims(task['id'], starttime=new_startime, endtime=new_endtime) + except Exception as e: + logger.error(e) + def onObservationStarted(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'active') + # otdb adjusts starttime when starting, + # reflect that in radb for pipelines + radb_task = self.radb.getTask(otdb_id=treeId) + if radb_task and radb_task['type'] == 'pipeline': + otdb_task = self.otdb.taskGetTreeInfo(otdb_id=treeId) + if otdb_task: + new_startime = otdb_task['starttime'] + new_endtime = new_startime + timedelta(seconds=radb_task['duration']) + + logger.info("Updating task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\'", radb_task['id'], treeId, new_startime, new_endtime) + self.radb.updateTaskAndResourceClaims(radb_task['id'], starttime=new_startime, endtime=new_endtime) + def onObservationCompleting(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'completing') + # otdb adjusts stoptime when completing, + self._updateStopTime(treeId, ['observation']) + + def _updateStopTime(self, treeId, task_types=None): + radb_task = self.radb.getTask(otdb_id=treeId) + if radb_task: + if task_types and radb_task['type'] not in task_types: + return + + otdb_task = self.otdb.taskGetTreeInfo(otdb_id=treeId) + if otdb_task and (otdb_task['starttime'] != radb_task['starttime'] or otdb_task['stoptime'] != radb_task['endtime']): + new_endtime = otdb_task['stoptime'] + + logger.info("Updating task %s (otdb_id=%s, status=%s) endtime to \'%s\'", radb_task['id'], treeId, radb_task['status'], new_endtime) + self.radb.updateTaskAndResourceClaims(radb_task['id'], endtime=new_endtime) + def onObservationFinished(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'finished') + # otdb adjusts stoptime when finishing, + # reflect that in radb for pipelines + self._updateStopTime(treeId, ['pipeline']) + def onObservationAborted(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'aborted') + # otdb adjusts stoptime when aborted, + self._updateStopTime(treeId) + + def main(): # Check the invocation arguments - parser = OptionParser("%prog [options]", - description='runs the resourceassignment database service') + parser = OptionParser("%prog [options]", description='runs the resourceassignment database service') parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') parser.add_option("--otdb_notification_busname", dest="otdb_notification_busname", type="string", default=DEFAULT_OTDB_NOTIFICATION_BUSNAME, diff --git a/SAS/ResourceAssignment/RAScripts/CMakeLists.txt b/SAS/ResourceAssignment/RAScripts/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..910b85a4f05b890c6a1c7fc17eca9aba9f1c9e35 --- /dev/null +++ b/SAS/ResourceAssignment/RAScripts/CMakeLists.txt @@ -0,0 +1,8 @@ +# $Id$ + +lofar_package(RAScripts 1.0 DEPENDS PyMessaging ResourceAssignmentService OTDB_Services pyparameterset) + +lofar_find_package(Python 2.6 REQUIRED) +include(PythonInstall) + +lofar_add_bin_scripts(povero) diff --git a/SAS/ResourceAssignment/RAScripts/povero b/SAS/ResourceAssignment/RAScripts/povero new file mode 100755 index 0000000000000000000000000000000000000000..8c9df128f620e10bc715428024df3dc30cc6b151 --- /dev/null +++ b/SAS/ResourceAssignment/RAScripts/povero @@ -0,0 +1,144 @@ +#!/usr/bin/python + +# Copyright (C) 2012-2015 ASTRON (Netherlands Institute for Radio Astronomy) +# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. + +# $Id: webservice.py 35176 2016-08-25 10:09:10Z schaap $ + +'''ResourceAssignmentEditor webservice serves a interactive html5 website for +viewing and editing lofar resources.''' + +import sys +from optparse import OptionParser +from datetime import datetime +import logging +import subprocess + +from lofar.parameterset import parameterset + +from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as DEFAULT_RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RADB_SERVICENAME + +from lofar.sas.otdb.otdbrpc import OTDBRPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME +from lofar.messaging import setQpidLogLevel + +logger = logging.getLogger(__name__) + +def getSlurmStats(otdb_id): + cmd = ['ssh', 'lofarsys@head01.cep4.control.lofar', 'sacct', '-o', 'jobid,cputimeraw,nnodes', '--name=%s' % otdb_id, '-S', '2016-01-01', '-X', '--parsable2', '-n'] + logger.debug(' '.join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + + if proc.returncode == 0: + try: + logger.debug(out.strip()) + lines = [l.strip() for l in out.strip().split('\n')] + last_job_line = lines[-1] + parts = last_job_line.split('|') + jobid = int(parts[0]) + cputimeraw = int(parts[1]) + nnodes = int(parts[2]) + clusterusage = nnodes / 50.0 + return jobid, cputimeraw, nnodes, clusterusage + except Exception as e: + logger.error(e) + else: + logger.error(err) + + return 0, 0, 0, 0 + +def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + + # Check the invocation arguments + parser = OptionParser('povero [options] <output_filename.csv>', + description='compute P over O for CEP4 pipelines') + parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') + parser.add_option('--radb_busname', dest='radb_busname', type='string', default=DEFAULT_RADB_BUSNAME, help='Name of the bus exchange on the qpid broker on which the radbservice listens, default: %default') + parser.add_option('--radb_servicename', dest='radb_servicename', type='string', default=DEFAULT_RADB_SERVICENAME, help='Name of the radbservice, default: %default') + parser.add_option('--otdb_busname', dest='otdb_busname', type='string', default=DEFAULT_OTDB_SERVICE_BUSNAME, help='Name of the bus exchange on the qpid broker on which the otdbservice listens, default: %default') + parser.add_option('--otdb_servicename', dest='otdb_servicename', type='string', default=DEFAULT_OTDB_SERVICENAME, help='Name of the otdbservice, default: %default') + parser.add_option('-o', '--otdb_id', dest='otdb_id', type='int', default=None, help='compute P/O for the given pipeline otdb_id') + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + (options, args) = parser.parse_args() + + if len(args) != 1: + print parser.usage + exit(1) + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', + level=logging.DEBUG if options.verbose else logging.INFO) + setQpidLogLevel(logging.INFO) + + ra = RARPC(busname=options.radb_busname, servicename=options.radb_servicename, broker=options.broker) + otdb = OTDBRPC(busname=options.otdb_busname, servicename=options.otdb_servicename, broker=options.broker) + + with ra, otdb: + pipelines = [ra.getTask(otdb_id=options.otdb_id)] if options.otdb_id else ra.getTasks(cluster='CEP4', task_type='pipeline', task_status='finished') + + with open(args[0], 'w') as csv_file: + line = "Project, OBS otdb_id, OBS name, #demix_sources, demix_sources, antennaArray, antennaSet, OBS starttime (UTC), OBS duration [s], PL slurm_id, PL otdb_id, PL name, #subbands, type, cluster usage [%], PL starttime (UTC), PL duration [s], PL duration_norm [s], P/O" + csv_file.write(line + "\n") + print line + + for i, pl in enumerate(pipelines): + jobid, cputimeraw, nnodes, clusterusage = getSlurmStats(pl['otdb_id']) + + if nnodes == 0: + continue + + pl_full_cluster_duration = int(clusterusage * pl['duration']) + + pl_parset = parameterset(otdb.taskGetSpecification(otdb_id=pl['otdb_id'])['specification']) + pl_name = pl_parset.getString('ObsSW.Observation.Scheduler.taskName', 'unknown') + pl_sub_type = pl_parset.getString('ObsSW.Observation.processSubtype') + dp_types = ['Correlated', 'CoherentStokes', 'IncoherentStokes'] + dp_type = [dp_type for dp_type in dp_types if pl_parset.getBool('ObsSW.Observation.DataProducts.Input_%s.enabled' % dp_type)][0] + pl_nrofsubbands = len(pl_parset.getString('ObsSW.Observation.DataProducts.Input_%s.filenames' % dp_type).split(',')) + pl_demix_sources = pl_parset.getString('ObsSW.Observation.ObservationControl.PythonControl.PreProcessing.demix_always', '') + if not pl_demix_sources: + pl_demix_sources = pl_parset.getString('ObsSW.Observation.ObservationControl.PythonControl.PreProcessing.demix_if_needed', '') + + if pl_demix_sources: + pl_demix_sources = pl_demix_sources.replace('[','').replace(']','') + pl_demix_sources = [x.strip() for x in pl_demix_sources.split(',')] if pl_demix_sources else [] + + pred_observations = ra.getTasks(task_ids=pl['predecessor_ids'], task_type='observation', task_status='finished') + + for pred_obs in pred_observations: + pred_obs_parset = parameterset(otdb.taskGetSpecification(otdb_id=pred_obs['otdb_id'])['specification']) + + obs_antennaArray = pred_obs_parset.getString('ObsSW.Observation.antennaArray') + obs_antennaSet = pred_obs_parset.getString('ObsSW.Observation.antennaSet') + projectName = pred_obs_parset.getString('ObsSW.Observation.Campaign.name', 'unknown') + obs_name = pred_obs_parset.getString('ObsSW.Observation.Scheduler.taskName', 'unknown') + + values = [projectName, pred_obs['otdb_id'], obs_name, len(pl_demix_sources), ';'.join(pl_demix_sources), obs_antennaArray, obs_antennaSet, pred_obs['starttime'], pred_obs['duration'], + jobid, pl['otdb_id'], pl_name, pl_nrofsubbands, pl_sub_type, 100.0*clusterusage, pl['starttime'], pl['duration'], pl_full_cluster_duration, '%.2f' % (pl_full_cluster_duration/pred_obs['duration'])] + line = ', '.join([str(x) for x in values]) + csv_file.write(line + '\n') + print '%.1f%%: ' % (100.0*i/len(pipelines)), line + + print "Done!" + +if __name__ == '__main__': + main() diff --git a/SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice.ini b/SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice.ini index 91f6b88fd5135fe96ae293ad23d52ec360dafd66..23d43f56ee0fa5cc956418d5ee77c97030e952eb 100644 --- a/SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice.ini +++ b/SAS/ResourceAssignment/RATaskSpecifiedService/bin/rataskspecifiedservice.ini @@ -1,5 +1,5 @@ [program:RATaskSpecified] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;rataskspecifiedservice' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec rataskspecifiedservice' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true diff --git a/SAS/ResourceAssignment/RATaskSpecifiedService/lib/RATaskSpecified.py b/SAS/ResourceAssignment/RATaskSpecifiedService/lib/RATaskSpecified.py index 493317ec635a327a534d0a3e4a0a92f4c30f8230..272145aa4ff1a6491bf97898ce9b7e03e04023ab 100755 --- a/SAS/ResourceAssignment/RATaskSpecifiedService/lib/RATaskSpecified.py +++ b/SAS/ResourceAssignment/RATaskSpecifiedService/lib/RATaskSpecified.py @@ -29,7 +29,7 @@ from lofar.messaging import FromBus, ToBus, EventMessage # RPC, from lofar.parameterset import PyParameterValue from lofar.sas.otdb.OTDBBusListener import OTDBBusListener from lofar.common.util import waitForInterrupt -from lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.otdbrpc import OTDBRPC +from lofar.sas.otdb.otdbrpc import OTDBRPC from lofar.sas.otdb.config import DEFAULT_OTDB_NOTIFICATION_BUSNAME, DEFAULT_OTDB_NOTIFICATION_SUBJECT from lofar.sas.resourceassignment.rataskspecified.config import DEFAULT_RA_TASK_SPECIFIED_NOTIFICATION_BUSNAME from lofar.sas.resourceassignment.rataskspecified.config import DEFAULT_RA_TASK_SPECIFIED_NOTIFICATION_SUBJECT @@ -108,6 +108,7 @@ def resourceIndicatorsFromParset( parsetDict ): add("Observation.VirtualInstrument.stationList", strvector) add("Observation.startTime") add("Observation.stopTime") + add("Observation.Scheduler.taskDuration") add("Observation.nrBeams") nrSAPs = int(get("Observation.nrBeams", 0)) @@ -119,6 +120,7 @@ def resourceIndicatorsFromParset( parsetDict ): # ===================================== add("Observation.DataProducts.Output_Correlated.enabled", bool) add("Observation.DataProducts.Output_Correlated.storageClusterName") + add("Observation.DataProducts.Output_Correlated.identifications", strvector) add("Observation.ObservationControl.OnlineControl.Cobalt.Correlator.integrationTime") add("Observation.ObservationControl.OnlineControl.Cobalt.Correlator.nrChannelsPerSubband") # TODO: We need a service that computes these 3 values @@ -132,8 +134,10 @@ def resourceIndicatorsFromParset( parsetDict ): # ===================================== add("Observation.DataProducts.Output_IncoherentStokes.enabled", bool) add("Observation.DataProducts.Output_IncoherentStokes.storageClusterName") + add("Observation.DataProducts.Output_IncoherentStokes.identifications", strvector) add("Observation.DataProducts.Output_CoherentStokes.enabled", bool) add("Observation.DataProducts.Output_CoherentStokes.storageClusterName") + add("Observation.DataProducts.Output_CoherentStokes.identifications", strvector) add("Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.flysEye", bool) #add("Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.nrChannelsPerSubband") # only needed to determine Cobalt.blockSize add("Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.subbandsPerFile") @@ -157,16 +161,23 @@ def resourceIndicatorsFromParset( parsetDict ): # Calibrator / Averaging pipelines add("Observation.DataProducts.Output_Correlated.enabled", bool) add("Observation.DataProducts.Output_Correlated.storageClusterName") + add("Observation.DataProducts.Output_Correlated.identifications", strvector) add("Observation.DataProducts.Output_InstrumentModel.enabled", bool) add("Observation.DataProducts.Output_InstrumentModel.storageClusterName") + add("Observation.DataProducts.Output_InstrumentModel.identifications", strvector) add("Observation.DataProducts.Input_Correlated.enabled", bool) - add("Observation.DataProducts.Input_Correlated.skip", intvector) - add("Observation.ObservationControl.PythonControl.DPPP.demixer.demixfreqstep") - add("Observation.ObservationControl.PythonControl.DPPP.demixer.demixtimestep") + add("Observation.DataProducts.Input_Correlated.identifications", strvector) + #We don't care about skip add("Observation.DataProducts.Input_Correlated.skip", intvector) + add("Observation.DataProducts.Input_InstrumentModel.enabled", bool) + add("Observation.DataProducts.Input_InstrumentModel.identifications", strvector) + #We don't care about skip add("Observation.DataProducts.Input_Correlated.skip", intvector) + add("Observation.ObservationControl.PythonControl.DPPP.demixer.freqstep") + add("Observation.ObservationControl.PythonControl.DPPP.demixer.timestep") # Imaging pipeline add("Observation.DataProducts.Output_SkyImage.enabled", bool) add("Observation.DataProducts.Output_SkyImage.storageClusterName") + add("Observation.DataProducts.Output_SkyImage.identifications", strvector) add("Observation.ObservationControl.PythonControl.Imaging.slices_per_image") add("Observation.ObservationControl.PythonControl.Imaging.subbands_per_image") @@ -177,10 +188,13 @@ def resourceIndicatorsFromParset( parsetDict ): # Pulsar pipeline add("Observation.DataProducts.Output_Pulsar.enabled", bool) add("Observation.DataProducts.Output_Pulsar.storageClusterName") + add("Observation.DataProducts.Output_Pulsar.identifications", strvector) add("Observation.DataProducts.Input_CoherentStokes.enabled", bool) - add("Observation.DataProducts.Input_CoherentStokes.skip", intvector) + add("Observation.DataProducts.Input_CoherentStokes.identifications", strvector) + #We don't care about skip add("Observation.DataProducts.Input_CoherentStokes.skip", intvector) add("Observation.DataProducts.Input_IncoherentStokes.enabled", bool) - add("Observation.DataProducts.Input_IncoherentStokes.skip", intvector) + add("Observation.DataProducts.Input_IncoherentStokes.identifications", strvector) + #We don't care about skip add("Observation.DataProducts.Input_IncoherentStokes.skip", intvector) return subset @@ -207,7 +221,8 @@ class RATaskSpecified(OTDBBusListener): self.send_bus.close() self.otdbrpc.close() - def get_predecessors(self, parset): + @staticmethod + def get_predecessors(parset): """ Extract the list of predecessor obs IDs from the given parset. """ key = PARSET_PREFIX + "Observation.Scheduler.predecessors" @@ -284,6 +299,10 @@ class RATaskSpecified(OTDBBusListener): logger.info("Result sent") def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + import logging import sys from optparse import OptionParser diff --git a/SAS/ResourceAssignment/RATaskSpecifiedService/test/tRATaskSpecified.py b/SAS/ResourceAssignment/RATaskSpecifiedService/test/tRATaskSpecified.py index 51349822cb3cfff2624d2ee2f2bfa81343d91459..3650d3b9a71d66bff9aa170acd4fe18c7eb4a3cd 100644 --- a/SAS/ResourceAssignment/RATaskSpecifiedService/test/tRATaskSpecified.py +++ b/SAS/ResourceAssignment/RATaskSpecifiedService/test/tRATaskSpecified.py @@ -8,13 +8,13 @@ from RATaskSpecified import * from RABusListener import RATaskSpecifiedBusListener from lofar.parameterset import PyParameterSet from lofar.messaging import EventMessage, Service +from lofar.common.methodtrigger import MethodTrigger from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICENAME import unittest from glob import glob import uuid import datetime -from threading import Condition, Lock import logging logging.basicConfig(stream=sys.stdout, level=logging.INFO) @@ -30,17 +30,17 @@ class TestGetPredecessors(unittest.TestCase): def test_0_predecessors(self): parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[]" } - self.assertEqual(predecessors(parset), []) + self.assertEqual(RATaskSpecified.get_predecessors(parset), []) def test_1_predecessor(self): parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[L426528]" } - self.assertEqual(predecessors(parset), [426528]) + self.assertEqual(RATaskSpecified.get_predecessors(parset), [{'id': 426528, 'source': 'otdb'}]) def test_2_predecessors(self): parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[L426528,L1]" } - self.assertEqual(sorted(predecessors(parset)), [1,426528]) + self.assertEqual(sorted(RATaskSpecified.get_predecessors(parset)), [{'id': 1, 'source': 'otdb'}, {'id': 426528, 'source': 'otdb'}]) def parset_as_dict(filename): @@ -74,6 +74,7 @@ class TestService(unittest.TestCase): self.busname = "%s-%s" % (sys.argv[0], str(uuid.uuid4())[:8]) self.bus = ToBus(self.busname, { "create": "always", "delete": "always", "node": { "type": "topic" } }) self.bus.open() + self.addCleanup(self.bus.close) # Define the services we use self.status_service = "%s/%s.TaskGetSpecification" % (self.busname,DEFAULT_OTDB_SERVICENAME) @@ -85,7 +86,7 @@ class TestService(unittest.TestCase): # Nr of parsets requested, to detect multiple requests for the same parset, or of superfluous parsets self.requested_parsets = 0 - def TaskSpecificationService( OtdbID ): + def TaskGetSpecificationService( OtdbID ): if OtdbID == 1: predecessors = "[2,3]" elif OtdbID == 2: @@ -103,44 +104,20 @@ class TestService(unittest.TestCase): PARSET_PREFIX + "Observation.Scheduler.predecessors": predecessors, } - self.parset_service = Service(DEFAULT_OTDB_SERVICENAME+".TaskGetSpecification", TaskSpecificationService, busname=self.busname) - self.parset_service.start_listening() + parset_service = Service(DEFAULT_OTDB_SERVICENAME+".TaskGetSpecification", TaskGetSpecificationService, busname=self.busname) + parset_service.start_listening() + self.addCleanup(parset_service.stop_listening) # ================================ # Setup listener to catch result # of our service # ================================ - class Listener(RATaskSpecifiedBusListener): - def __init__(self, **kwargs): - super(Listener, self).__init__(**kwargs) + listener = RATaskSpecifiedBusListener(busname=self.busname) + listener.start_listening() + self.addCleanup(listener.stop_listening) - self.messageReceived = False - self.lock = Lock() - self.cond = Condition(self.lock) - - def onTaskSpecified(self, sasId, modificationTime, resourceIndicators): - self.messageReceived = True - - self.sasID = sasId - self.resourceIndicators = resourceIndicators - - # Release waiting parent - with self.lock: - self.cond.notify() - - def waitForMessage(self): - with self.lock: - self.cond.wait(5.0) - return self.messageReceived - - self.listener = Listener(busname=self.busname) - self.listener.start_listening() - - def tearDown(self): - self.listener.stop_listening() - self.parset_service.stop_listening() - self.bus.close() + self.trigger = MethodTrigger(listener, "onTaskSpecified") def testNoPredecessors(self): """ @@ -161,13 +138,14 @@ class TestService(unittest.TestCase): tb.send(msg) # Wait for message to arrive - self.assertTrue(self.listener.waitForMessage()) + self.assertTrue(self.trigger.wait()) # Verify message - self.assertEqual(self.listener.sasID, 3) - self.assertNotIn("1", self.listener.resourceIndicators); - self.assertNotIn("2", self.listener.resourceIndicators); - self.assertIn("3", self.listener.resourceIndicators); + self.assertEqual(self.trigger.args[0], 3) + resourceIndicators = self.trigger.args[2] + self.assertNotIn("1", resourceIndicators) + self.assertNotIn("2", resourceIndicators) + self.assertIn("3", resourceIndicators); # Make sure we only requested one parset self.assertEqual(self.requested_parsets, 1) @@ -192,21 +170,25 @@ class TestService(unittest.TestCase): tb.send(msg) # Wait for message to arrive - self.assertTrue(self.listener.waitForMessage()) + self.assertTrue(self.trigger.wait()) # Verify message - self.assertEqual(self.listener.sasID, 1) - self.assertIn("1", self.listener.resourceIndicators); - self.assertIn("2", self.listener.resourceIndicators); - self.assertIn("3", self.listener.resourceIndicators); + self.assertEqual(self.trigger.args[0], 1) + resourceIndicators = self.trigger.args[2] + self.assertIn("1", resourceIndicators); + self.assertIn("2", resourceIndicators); + self.assertIn("3", resourceIndicators); # Make sure we only requested exactly three parsets self.assertEqual(self.requested_parsets, 3) def main(argv): - unittest.main(verbosity=2) + unittest.main() if __name__ == "__main__": # run all tests import sys + #TODO: Fix this test + logger.error("FIX THIS TEST") + sys.exit(3) main(sys.argv[1:]) diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/CMakeLists.txt b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/CMakeLists.txt index e652fe295b5fe6a754f5bf4adfa8bb2cd6c20c52..c2f60a22d1dad738cffdb80772a362fd0f8d2885 100644 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/CMakeLists.txt +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/CMakeLists.txt @@ -1,6 +1,6 @@ # $Id: CMakeLists.txt 30355 2014-11-04 13:46:05Z loose $ -lofar_package(RAtoOTDBTaskSpecificationPropagator 0.1 DEPENDS PyMessaging PyCommon pyparameterset) +lofar_package(RAtoOTDBTaskSpecificationPropagator 0.1 DEPENDS PyMessaging PyCommon pyparameterset OTDB_Services) include(PythonInstall) set(USE_PYTHON_COMPILATION Off) diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/bin/rotspservice.ini b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/bin/rotspservice.ini index ff98cf9f2089426350dc68cadc09b056b4032f69..15653437901b7614d1e62af1a3b88ace6273658d 100644 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/bin/rotspservice.ini +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/bin/rotspservice.ini @@ -1,5 +1,5 @@ [program:rotspservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;rotspservice' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec rotspservice' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/CMakeLists.txt b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/CMakeLists.txt index 7713a14080bb4ff22b0dd6f1b5120738aec341ed..fb4a42bf7a69daaeabb27146aea4f29d9ed298f1 100644 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/CMakeLists.txt +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/CMakeLists.txt @@ -6,6 +6,5 @@ python_install( translator.py config.py propagator.py - otdbrpc.py DESTINATION lofar/sas/resourceassignment/ratootdbtaskspecificationpropagator) diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/propagator.py b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/propagator.py index 725f248142e515c3fe3b74fc949e9276573075c8..42cf5c22b21415f0597e49dbaf80b1eb39433fd6 100755 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/propagator.py +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/propagator.py @@ -28,6 +28,7 @@ reads the info from the RA DB and sends it to OTDB in the correct format. import logging import datetime import time +import pprint from lofar.messaging.RPC import RPC, RPCException from lofar.parameterset import parameterset @@ -36,7 +37,7 @@ from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC as from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME -from lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.otdbrpc import OTDBRPC +from lofar.sas.otdb.otdbrpc import OTDBRPC from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME from lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.translator import RAtoOTDBTranslator @@ -109,6 +110,16 @@ class RAtoOTDBPropagator(): except Exception as e: logger.error(e) + def doTaskError(self, otdb_id): + logger.info('doTaskError: otdb_id=%s' % (otdb_id,)) + if not otdb_id: + logger.warning('doTaskError no valid otdb_id: otdb_id=%s' % (otdb_id,)) + return + try: + self.otdbrpc.taskSetStatus(otdb_id, 'error') + except Exception as e: + logger.error(e) + def doTaskScheduled(self, ra_id, otdb_id, mom_id): try: logger.info('doTaskScheduled: ra_id=%s otdb_id=%s mom_id=%s' % (ra_id, otdb_id, mom_id)) @@ -133,15 +144,64 @@ class RAtoOTDBPropagator(): project_name = "_".join(project[str(mom_id)]['project_name'].split()) except (RPCException, KeyError) as e: logger.error('Could not get project name from MoM for mom_id %s: %s' % (mom_id, str(e))) - logger.info('Using \'unknown\' as project name.') + logger.info("Using 'unknown' as project name.") project_name = 'unknown' otdb_info = self.translator.CreateParset(otdb_id, ra_info, project_name) - logger.debug("Parset info for OTDB: %s" %otdb_info) self.setOTDBinfo(otdb_id, otdb_info, 'scheduled') except Exception as e: logger.error(e) - self.doTaskConflict(otdb_id) + self.doTaskError(otdb_id) #FIXME should be to the RADB also or instead? + + def ParseStorageProperties(self, storage_claim): + """input something like: + {u'username':u'anonymous', u'status': u'allocated', u'resource_name': + u'cep4storage', u'user_id': -1, u'resource_type_id': 5, u'task_id': 6349, + u'status_id': 1, u'resource_id': 117, u'session_id': 1, u'id': 339, + u'claim_size': 24000, u'starttime': datetime.datetime(2016, 6, 10, 16, 8, 15), + u'resource_type_name': u'storage', u'endtime': datetime.datetime(2016, 7, 11, + 16, 33, 15), u'properties': [{u'io_type_name': u'output', u'type_name': + u'img_file_size', u'value': 1000, u'io_type_id': 0, u'type_id': 12, u'id': 808}, + {u'io_type_name': u'output', u'type_name': u'nr_of_img_files', u'value': 24, + u'io_type_id': 0, u'type_id': 4, u'id': 809}, {u'io_type_name': u'input', + u'type_name': u'nr_of_uv_files', u'value': 240, u'io_type_id': 1, u'type_id': 2, + u'id': 810}, {u'io_type_name': u'input', u'type_name': u'uv_file_size', + u'value': 43957416, u'io_type_id': 1, u'type_id': 10, u'id': 811}]} + output something like: + {'output_files': {u'nr_of_im_files': 488, u'nr_of_uv_files': 488, u'im_file_size': 1000, u'uv_file_size': 32500565}} + """ + result = {'input_files': {}, 'output_files': {}} + # FIXME This is very fragile code, mainly because we don't know if 'saps' are part of the input or output. + # This should probably be redesigned, but might require changes in how RADB works. + if 'saps' in storage_claim: + input = False + output = False + saps = [] + for s in storage_claim['saps']: + properties = {} + for p in s['properties']: + if p['io_type_name'] == 'output': + properties[p['type_name']] = p['value'] + output = True + if p['io_type_name'] == 'input': + properties[p['type_name']] = p['value'] + input = True + if input or output: + saps.append({'sap_nr' : s['sap_nr'], 'properties': properties}) + if input: + if saps: + result['input_files']['saps'] = saps + if output: + if saps: + result['output_files']['saps'] = saps + if 'properties' in storage_claim: + for p in storage_claim['properties']: + if p['io_type_name'] == 'output': + result['output_files'][p['type_name']] = p['value'] + if p['io_type_name'] == 'input': + result['input_files'][p['type_name']] = p['value'] + logger.info(pprint.pformat(result)) + return result def getRAinfo(self, ra_id): info = {} @@ -149,21 +209,23 @@ class RAtoOTDBPropagator(): task = self.radbrpc.getTask(ra_id) claims = self.radbrpc.getResourceClaims(task_ids=ra_id, extended=True, include_properties=True) for claim in claims: - logger.debug("Processing claim: %s" % claim) - if claim['resource_type_name'] == 'storage': - info['storage'] = claim + logger.info("Processing claim: %s" % claim) + if claim['resource_type_name'] == 'storage': ## TODO we will need to check for different storage names/types in the future + info['storage'] = self.ParseStorageProperties(claim) info["starttime"] = task["starttime"] info["endtime"] = task["endtime"] info["status"] = task["status"] + info["type"] = task["type"] return info def setOTDBinfo(self, otdb_id, otdb_info, otdb_status): try: - logger.info('Setting specticication for otdb_id %s: %s' % (otdb_id, otdb_info)) + logger.info('Setting specticication for otdb_id %s:\n' % (otdb_id,)) + logger.info(pprint.pformat(otdb_info)) self.otdbrpc.taskSetSpecification(otdb_id, otdb_info) self.otdbrpc.taskPrepareForScheduling(otdb_id, otdb_info["LOFAR.ObsSW.Observation.startTime"], otdb_info["LOFAR.ObsSW.Observation.stopTime"]) logger.info('Setting status (%s) for otdb_id %s' % (otdb_status, otdb_id)) self.otdbrpc.taskSetStatus(otdb_id, otdb_status) except Exception as e: logger.error(e) - self.doTaskConflict(otdb_id) + self.doTaskError(otdb_id) #FIXME should be to the RADB also or instead? diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/rotspservice.py b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/rotspservice.py index d67c9b37e8de4689d1218586f2b66c1f6f89b414..e66822e21b6b73795a084454955cc70cd87933bd 100755 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/rotspservice.py +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/rotspservice.py @@ -35,17 +35,16 @@ import time from lofar.messaging.RPC import RPC, RPCException import lofar.sas.resourceassignment.resourceassignmentservice.rpc as rarpc ## RA DB -from lofar.sas.resourceassignment.database.radbbuslistener import RADBBusListener -from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_BUSNAME as RA_NOTIFICATION_BUSNAME -from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_PREFIX as RA_NOTIFICATION_PREFIX +from lofar.sas.resourceassignment.resourceassigner.rabuslistener import RABusListener +from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_BUSNAME, DEFAULT_RA_NOTIFICATION_SUBJECTS, DEFAULT_RA_NOTIFICATION_PREFIX from lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.propagator import RAtoOTDBPropagator logger = logging.getLogger(__name__) -class RATaskStatusChangedListener(RADBBusListener): +class RATaskStatusChangedListener(RABusListener): def __init__(self, - busname=RA_NOTIFICATION_BUSNAME, - subject=RA_NOTIFICATION_PREFIX + 'TaskUpdated', + busname=DEFAULT_RA_NOTIFICATION_BUSNAME, + subject=DEFAULT_RA_NOTIFICATION_SUBJECTS, broker=None, propagator=None, ## TODO also give translator? **kwargs): @@ -65,37 +64,38 @@ class RATaskStatusChangedListener(RADBBusListener): if not self.propagator: self.propagator = RAtoOTDBPropagator() - def onTaskUpdated(self, old_task, new_task): - # override super onTaskUpdated - # check for status change, and call either onTaskScheduled or onTaskScheduled - if old_task['status_id'] != new_task['status_id']: - if new_task['status'] == 'scheduled': - self.onTaskScheduled(new_task['id'], new_task['otdb_id'], new_task['mom_id']) - elif new_task['status'] == 'conflict': - self.onTaskConflict(new_task['id'], new_task['otdb_id'], new_task['mom_id']) + def onTaskScheduled(self, task_ids): + radb_id = task_ids.get('radb_id') + otdb_id = task_ids.get('otdb_id') + mom_id = task_ids.get('mom_id') + logger.info('onTaskScheduled: radb_id=%s otdb_id=%s mom_id=%s', radb_id, otdb_id, mom_id) - def onTaskInserted(self, new_task): - # override super onTaskInserted - # check for status, and call either onTaskScheduled or onTaskScheduled - if new_task['status'] == 'scheduled': - self.onTaskScheduled(new_task['id'], new_task['otdb_id'], new_task['mom_id']) - elif new_task['status'] == 'conflict': - self.onTaskConflict(new_task['id'], new_task['otdb_id'], new_task['mom_id']) + self.propagator.doTaskScheduled(radb_id, otdb_id, mom_id) - def onTaskScheduled(self, ra_id, otdb_id, mom_id): - logger.info('onTaskScheduled: ra_id=%s otdb_id=%s mom_id=%s' % (ra_id, otdb_id, mom_id)) + def onTaskConflict(self, task_ids): + radb_id = task_ids.get('radb_id') + otdb_id = task_ids.get('otdb_id') #Does this work if one of the Id's is not set? + mom_id = task_ids.get('mom_id') + logger.info('onTaskConflict: radb_id=%s otdb_id=%s mom_id=%s', radb_id, otdb_id, mom_id) - self.propagator.doTaskScheduled(ra_id, otdb_id, mom_id) + self.propagator.doTaskConflict(otdb_id) - def onTaskConflict(self, ra_id, otdb_id, mom_id): - logger.info('onTaskConflict: ra_id=%s otdb_id=%s mom_id=%s' % (ra_id, otdb_id, mom_id)) + def onTaskError(self, task_ids): + radb_id = task_ids.get('radb_id') + otdb_id = task_ids.get('otdb_id') #Does this work if one of the Id's is not set? + mom_id = task_ids.get('mom_id') + logger.info('onTaskError: radb_id=%s otdb_id=%s mom_id=%s', radb_id, otdb_id, mom_id) - self.propagator.doTaskConflict(ra_id, otdb_id, mom_id) + self.propagator.doTaskError(otdb_id) __all__ = ["RATaskStatusChangedListener"] def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + from optparse import OptionParser from lofar.messaging import setQpidLogLevel from lofar.common.util import waitForInterrupt @@ -111,8 +111,8 @@ def main(): parser = OptionParser("%prog [options]", description='runs the RAtoOTDBTaskSpecificationPropagator service') parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') - parser.add_option("--notification_busname", dest="notification_busname", type="string", default=RA_NOTIFICATION_BUSNAME, help="Name of the notification bus on which messages are published, default: %default") - parser.add_option("--notification_subject", dest="notification_subject", type="string", default=RA_NOTIFICATION_PREFIX+'TaskUpdated', help="Subject of the published messages to listen for, default: %default") + parser.add_option("--ra_notification_busname", dest="ra_notification_busname", type="string", default=DEFAULT_RA_NOTIFICATION_BUSNAME, help="Name of the notification bus on which messages are published, default: %default") + parser.add_option("--ra_notification_prefix", dest="ra_notification_prefix", type="string", default=DEFAULT_RA_NOTIFICATION_PREFIX, help="Prefix of the subjects of the published messages to listen for, default: %default") parser.add_option("--radb_busname", dest="radb_busname", type="string", default=RADB_BUSNAME, help="Name of the bus on which the RADB service listens, default: %default") parser.add_option("--radb_servicename", dest="radb_servicename", type="string", default=RADB_SERVICENAME, help="Name of the RADB service, default: %default") parser.add_option("--otdb_busname", dest="otdb_busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, help="Name of the bus on which the OTDB service listens, default: %default") @@ -134,8 +134,8 @@ def main(): mom_busname=options.mom_busname, mom_servicename=options.mom_servicename, broker=options.broker) as propagator: - with RATaskStatusChangedListener(busname=options.notification_busname, - subject=options.notification_subject, + with RATaskStatusChangedListener(busname=options.ra_notification_busname, + subject=options.ra_notification_prefix + '*', broker=options.broker, propagator=propagator) as listener: waitForInterrupt() diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/translator.py b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/translator.py index 3a70d827f69b5e6f71a0ed7ec00dd9afd6032f96..e8411ba0f5ac47afa27eff8f8f7c327ac903d45c 100755 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/translator.py +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/lib/translator.py @@ -26,6 +26,7 @@ reads the info from the RA DB and sends it to OTDB in the correct format. """ import logging +import pprint from lofar.common.util import to_csv_string from math import ceil, floor @@ -59,137 +60,162 @@ class RAtoOTDBTranslator(): '''returns the full path to the data dir on CEP4 for give project and otdb_id, depending on environment''' return "%s/%s/L%d" % (self.cep4DataPath(), project_name, otdb_id) - def CreateCorrelated(self, otdb_id, storage_properties, project_name): - sb_nr = 0 + def CreateCorrelated(self, otdb_id, storage_properties, project_name, io_type, sb_nr = 0): locations = [] filenames = [] + skip = [] result = {} - for sap in storage_properties["saps"]: ##We might need to sort saps? - logging.debug('processing sap: %s' % sap) - if "nr_of_uv_files" in sap['properties']: - for _ in xrange(sap['properties']['nr_of_uv_files']): - locations.append(self.locationPath(project_name, otdb_id)) - filenames.append("L%d_SAP%03d_SB%03d_uv.MS" % (otdb_id, sap['sap_nr'], sb_nr)) - sb_nr += 1 - result[PREFIX + 'DataProducts.Output_Correlated.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_Correlated.filenames'] = '[' + to_csv_string(filenames) + ']' + if 'saps' in storage_properties: ## It's an observation + for sap in storage_properties["saps"]: ##We might need to sort saps? + logging.debug('processing sap: %s' % sap) + if "nr_of_uv_files" in sap['properties']: + if 'start_sb_nr' in sap['properties']: + sb_nr = sap['properties']['start_sb_nr'] + for _ in xrange(sap['properties']['nr_of_uv_files']): + locations.append(self.locationPath(project_name, otdb_id) + '/uv') + filenames.append("L%d_SAP%03d_SB%03d_uv.MS" % (otdb_id, sap['sap_nr'], sb_nr)) + skip.append("0") + sb_nr += 1 + else: ## It's a pipeline (no SAPs) + if 'start_sb_nr' in storage_properties: + sb_nr = storage_properties['start_sb_nr'] + filename_template = "L%d_SB%03d_uv.MS" + elif 'start_sbg_nr' in storage_properties: #Right now we're assuming this doesn't happen on raw data! + sb_nr = storage_properties['start_sbg_nr'] + filename_template = "L%d_SBG%03d_uv.MS" + for _ in xrange(storage_properties['nr_of_uv_files']): + locations.append(self.locationPath(project_name, otdb_id) + '/uv') + filenames.append(filename_template % (otdb_id, sb_nr)) + skip.append("0") + sb_nr += 1 + result[PREFIX + 'DataProducts.%s_Correlated.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_Correlated.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_Correlated.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - def CreateCoherentStokes(self, otdb_id, storage_properties, project_name): - SB_nr = 0 + def CreateCoherentStokes(self, otdb_id, storage_properties, project_name, io_type): locations = [] filenames = [] + skip = [] result = {} nr_stokes = storage_properties['nr_of_cs_stokes'] for sap in storage_properties["saps"]: ##We might need to sort saps? - if "nr_of_cs_files" in sap['properties']: + if 'nr_of_cs_files' in sap['properties']: nr_files = sap['properties']['nr_of_cs_files'] nr_tabs = sap['properties']['nr_of_tabs'] + skip_tab = 'is_tab_nr' in sap['properties'] nr_parts = int(ceil(nr_files/float(nr_tabs * nr_stokes))) for tab in xrange(nr_tabs): + if skip_tab: + if tab == sap['properties']['is_tab_nr']: + continue for stokes in xrange(nr_stokes): for part in xrange(nr_parts): - locations.append(self.locationPath(project_name, otdb_id)) + locations.append(self.locationPath(project_name, otdb_id) + '/cs') filenames.append("L%d_SAP%03d_B%03d_S%d_P%03d_bf.h5" % (otdb_id, sap['sap_nr'], tab, stokes, part)) - result[PREFIX + 'DataProducts.Output_CoherentStokes.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_CoherentStokes.filenames'] = '[' + to_csv_string(filenames) + ']' + skip.append("0") + result[PREFIX + 'DataProducts.%s_CoherentStokes.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_CoherentStokes.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_CoherentStokes.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - def CreateIncoherentStokes(self, otdb_id, storage_properties, project_name): - SB_nr = 0 + def CreateIncoherentStokes(self, otdb_id, storage_properties, project_name, io_type): locations = [] filenames = [] + skip = [] result = {} nr_stokes = storage_properties['nr_of_is_stokes'] for sap in storage_properties["saps"]: ##We might need to sort saps? if "nr_of_is_files" in sap['properties']: - nr_files = sap['properties']['nr_of_is_files'] - nr_tabs = sap['properties']['nr_of_tabs'] - nr_parts = int(ceil(nr_files/float(nr_tabs * nr_stokes))) - for tab in xrange(nr_tabs): - for stokes in xrange(nr_stokes): - for part in xrange(nr_parts): - locations.append(self.locationPath(project_name, otdb_id)) - filenames.append("L%d_SAP%03d_B%03d_S%d_P%03d_bf.h5" % (otdb_id, sap['sap_nr'], tab, stokes, part)) - result[PREFIX + 'DataProducts.Output_IncoherentStokes.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_IncoherentStokes.filenames'] = '[' + to_csv_string(filenames) + ']' + nr_files = sap['properties']['nr_of_is_files'] + is_tab_nr = sap['properties']['is_tab_nr'] + nr_parts = int(ceil(nr_files/float(nr_stokes))) + for stokes in xrange(nr_stokes): + for part in xrange(nr_parts): + locations.append(self.locationPath(project_name, otdb_id) + '/is') + filenames.append("L%d_SAP%03d_B%03d_S%d_P%03d_bf.h5" % (otdb_id, sap['sap_nr'], is_tab_nr, stokes, part)) + skip.append("0") + result[PREFIX + 'DataProducts.%s_IncoherentStokes.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_IncoherentStokes.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_IncoherentStokes.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - def CreateCreateInstrumentModel(self, otdb_id, storage_properties, project_name): - SB_nr = 0 + def CreateInstrumentModel(self, otdb_id, storage_properties, project_name, io_type, sb_nr = 0): locations = [] filenames = [] + skip = [] result = {} - for sap in storage_properties["saps"]: ##We might need to sort saps? - if "nr_of_im_files" in sap['properties']: - for _ in range(sap['properties']['nr_of_im_files']): - locations.append(self.locationPath(project_name, otdb_id)) - filenames.append("L%d_SAP%03d_SB%03d_inst.INST" % (otdb_id, sap['sap_nr'], sb_nr)) - result[PREFIX + 'DataProducts.Output_InstrumentModel.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_InstrumentModel.filenames'] = '[' + to_csv_string(filenames) + ']' + if 'start_sb_nr' in storage_properties: + sb_nr = storage_properties['start_sb_nr'] + for _ in xrange(storage_properties['nr_of_im_files']): + locations.append(self.locationPath(project_name, otdb_id) + '/im') + filenames.append("L%d_SB%03d_inst.INST" % (otdb_id, sb_nr)) + skip.append("0") + sb_nr += 1 + result[PREFIX + 'DataProducts.%s_InstrumentModel.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_InstrumentModel.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_InstrumentModel.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - def CreateSkyImage(self, otdb_id, storage_properties, project_name): - SB_nr = 0 + def CreateSkyImage(self, otdb_id, storage_properties, project_name, io_type): + sbg_nr = 0 locations = [] filenames = [] + skip = [] result = {} - for sap in storage_properties["saps"]: ##We might need to sort saps? - if "nr_of_img_files" in sap['properties']: - for _ in range(sap['properties']['nr_of_img_files']): - locations.append(self.locationPath(project_name, otdb_id)) - filenames.append("L%d_SAP%03d_SB%03d_sky.IM" % (otdb_id, sap['sap_nr'], sb_nr)) - result[PREFIX + 'DataProducts.Output_SkyImage.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_SkyImage.filenames'] = '[' + to_csv_string(filenames) + ']' + for _ in xrange(storage_properties['nr_of_img_files']): + locations.append(self.locationPath(project_name, otdb_id) + '/img') + filenames.append("L%d_SBG%03d_sky.IM" % (otdb_id, sbg_nr)) + skip.append("0") + sbg_nr += 1 + result[PREFIX + 'DataProducts.%s_SkyImage.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_SkyImage.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_SkyImage.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - def CreatePulsarPipeline(self, otdb_id, storage_properties, project_name): - SB_nr = 0 + def CreatePulsarPipeline(self, otdb_id, storage_properties, project_name, io_type): + p_nr = 0 locations = [] filenames = [] + skip = [] result = {} - for sap in storage_properties["saps"]: ##We might need to sort saps? - if "nr_of_uv_files" in sap['properties']: - for _ in range(sap['properties']['nr_of_pulp_files']): - locations.append(self.locationPath(project_name, otdb_id)) - filenames.append("L%d_SAP%03d_SB%03d_bf.h5" % (otdb_id, sap['sap_nr'], sb_nr)) - result[PREFIX + 'DataProducts.Output_Pulsar.locations'] = '[' + to_csv_string(locations) + ']' - result[PREFIX + 'DataProducts.Output_Pulsar.filenames'] = '[' + to_csv_string(filenames) + ']' + for _ in range(storage_properties['nr_of_pulp_files']): + locations.append(self.locationPath(project_name, otdb_id) + '/pulp') + filenames.append("L%d_P%03d_pulp.tgz" % (otdb_id, p_nr)) + skip.append("0") + p_nr += 1 + result[PREFIX + 'DataProducts.%s_Pulsar.locations' % (io_type)] = '[' + to_csv_string(locations) + ']' + result[PREFIX + 'DataProducts.%s_Pulsar.filenames' % (io_type)] = '[' + to_csv_string(filenames) + ']' + result[PREFIX + 'DataProducts.%s_Pulsar.skip' % (io_type)] = '[' + to_csv_string(skip) + ']' return result - - def CreateStorageKeys(self, otdb_id, storage_properties, project_name): - logging.debug(otdb_id, storage_properties) + def CreateStorageKeys(self, otdb_id, storage_properties, project_name, io_type): result = {} if 'nr_of_uv_files' in storage_properties: - result.update(self.CreateCorrelated(otdb_id, storage_properties, project_name)) + result.update(self.CreateCorrelated(storage_properties['uv_otdb_id'], storage_properties, project_name, io_type)) if 'nr_of_cs_files' in storage_properties: - result.update(self.CreateCoherentStokes(otdb_id, storage_properties, project_name)) + result.update(self.CreateCoherentStokes(storage_properties['cs_otdb_id'], storage_properties, project_name, io_type)) if 'nr_of_is_files' in storage_properties: - result.update(self.CreateIncoherentStokes(otdb_id, storage_properties, project_name)) + result.update(self.CreateIncoherentStokes(storage_properties['is_otdb_id'], storage_properties, project_name, io_type)) if 'nr_of_im_files' in storage_properties: - result.update(self.CreateInstrumentModel(otdb_id, storage_properties, project_name)) + result.update(self.CreateInstrumentModel(storage_properties['im_otdb_id'], storage_properties, project_name, io_type)) if 'nr_of_img_files' in storage_properties: - result.update(self.CreateSkyImage(otdb_id, storage_properties, project_name)) + result.update(self.CreateSkyImage(storage_properties['img_otdb_id'], storage_properties, project_name, io_type)) if 'nr_of_pulp_files' in storage_properties: - result.update(self.CreatePulsarPipeline(otdb_id, storage_properties, project_name)) + result.update(self.CreatePulsarPipeline(storage_properties['pulp_otdb_id'], storage_properties, project_name, io_type)) return result - def parseStorageProperties(self, storage_claim): + def ProcessStorageInfo(self, otdb_id, storage_info, project_name): + logging.info('processing the storage for %i' % (otdb_id)) result = {} - result['saps'] = [] - if 'saps' in storage_claim: - for s in storage_claim['saps']: - properties = {} - for p in s['properties']: - properties[p['type_name']] = p['value'] - result['saps'].append({'sap_nr' : s['sap_nr'], 'properties': properties}) - for p in storage_claim['properties']: - result[p['type_name']] = p['value'] + if 'input_files' in storage_info: + result.update(self.CreateStorageKeys(otdb_id, storage_info['input_files'], project_name, "Input")) + if 'output_files' in storage_info: + result.update(self.CreateStorageKeys(otdb_id, storage_info['output_files'], project_name, "Output")) return result def CreateParset(self, otdb_id, ra_info, project_name): - logger.info('CreateParset: start=%s, end=%s' % (ra_info['starttime'], ra_info['endtime'])) + logger.info('CreateParset for %s with start=%s, end=%s' % (otdb_id, ra_info['starttime'], ra_info['endtime'])) parset = {} #parset[PREFIX+'momID'] = str(mom_id) @@ -197,10 +223,20 @@ class RAtoOTDBTranslator(): parset[PREFIX+'stopTime'] = ra_info['endtime'].strftime('%Y-%m-%d %H:%M:%S') if 'storage' in ra_info: - logging.debug(ra_info['storage']) - parset.update(self.CreateStorageKeys(otdb_id, self.parseStorageProperties(ra_info['storage']), project_name)) + logging.info("Adding storage claims to parset\n" + pprint.pformat(ra_info['storage'])) + parset.update(self.ProcessStorageInfo(otdb_id, ra_info['storage'], project_name)) if 'stations' in ra_info: + logging.info("Adding stations to parset: " + str(ra_info["stations"])) parset[PREFIX+'VirtualInstrument.stationList'] = ra_info["stations"] + if ra_info['type'] == 'observation': + logging.info("Adding inspection plot commands to parset") + parset[PREFIX+'ObservationControl.OnlineControl.inspectionHost'] = 'head01.cep4.control.lofar' + parset[PREFIX+'ObservationControl.OnlineControl.inspectionProgram'] = 'inspection-plots-observation.sh' + + #special case for dynspec projects for Richard Fallows + if project_name in ['LC6_001','IPS_Commissioning']: + parset[PREFIX+'ObservationControl.OnlineControl.inspectionProgram'] = '/data/home/lofarsys/dynspec/scripts/inspection-dynspec-observation.sh' + return parset diff --git a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/test/t_rotspservice.py b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/test/t_rotspservice.py index 19e4f19a2cdee3ddd6d40ada7baf166a261c87fe..1e49b01e28d6a0f1ab09a39e2ea4671c3dafefb2 100755 --- a/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/test/t_rotspservice.py +++ b/SAS/ResourceAssignment/RAtoOTDBTaskSpecificationPropagator/test/t_rotspservice.py @@ -42,10 +42,6 @@ with patch('lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.rpc #give pre-cooked answer depending on called service if servicename == 'ResourceEstimator': return {'Observation':{'total_data_size':1, 'total_bandwidth':1, 'output_files':1}}, "OK" - elif servicename == 'SSDBService.GetActiveGroupNames': - return {0:'storagenodes', 1:'computenodes', 2:'archivenodes', 3:'locusnodes', 4:'cep4'}, "OK" - elif servicename == 'SSDBService.GetHostForGID': - return {u'groupname': u'cep4', u'nodes': [{u'claimedspace': 0, u'totalspace': 702716, u'statename': u'Active', u'usedspace': 23084, u'id': 1, u'groupname': u'cep4', u'path': u'/lustre', u'hostname': u'lustre001'}]}, "OK" return None, None @@ -69,4 +65,4 @@ with patch('lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.rpc ##TODO: added test asserts etc - #unittest.main(verbosity=2) + #unittest.main() diff --git a/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt index e78a2d4035fd64feec25aa3ade452677df9e061b..1f76919118f14bb90c953c262c326cc70bc96caa 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt @@ -1,6 +1,6 @@ # $Id: CMakeLists.txt 30355 2014-11-04 13:46:05Z loose $ -lofar_package(ResourceAssigner 0.1 DEPENDS PyMessaging PyCommon pyparameterset) +lofar_package(ResourceAssigner 0.1 DEPENDS PyMessaging PyCommon pyparameterset OTDB_Services) include(PythonInstall) set(USE_PYTHON_COMPILATION Off) diff --git a/SAS/ResourceAssignment/ResourceAssigner/bin/resourceassigner.ini b/SAS/ResourceAssignment/ResourceAssigner/bin/resourceassigner.ini index a144ded3d438cf63137bcaf97bc96b6f605682b1..bba8fe5d9318ef72e4b83a52d2fe6a40802e15d8 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/bin/resourceassigner.ini +++ b/SAS/ResourceAssignment/ResourceAssigner/bin/resourceassigner.ini @@ -1,5 +1,5 @@ [program:resourceassigner] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;resourceassigner' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec resourceassigner' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssigner/lib/CMakeLists.txt index 658a170041262a2c9dcd476386390870c97aba0c..c8aac82b1cc11782ba0026d4bf04f02239bf35a6 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/CMakeLists.txt @@ -4,6 +4,8 @@ python_install( __init__.py raservice.py assignment.py + rabuslistener.py + schedulechecker.py config.py DESTINATION lofar/sas/resourceassignment/resourceassigner) diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/assignment.py b/SAS/ResourceAssignment/ResourceAssigner/lib/assignment.py index f5ee34772c4442f7a30345fc918ade92a703bb40..a2fd3f2e7ddc475eba9b9a0c601f49640670d325 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/assignment.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/assignment.py @@ -30,9 +30,13 @@ import time import collections from lofar.common.util import humanreadablesize +from lofar.messaging.messages import EventMessage +from lofar.messaging.messagebus import ToBus from lofar.messaging.RPC import RPC, RPCException from lofar.parameterset import parameterset +from lofar.sas.resourceassignment.resourceassigner.schedulechecker import movePipelineAfterItsPredecessors + from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as RADB_BUSNAME from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as RADB_SERVICENAME @@ -40,12 +44,18 @@ from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAUL from lofar.sas.resourceassignment.resourceassignmentestimator.config import DEFAULT_BUSNAME as RE_BUSNAME from lofar.sas.resourceassignment.resourceassignmentestimator.config import DEFAULT_SERVICENAME as RE_SERVICENAME -from lofar.sas.resourceassignment.ratootdbtaskspecificationpropagator.otdbrpc import OTDBRPC +from lofar.sas.otdb.otdbrpc import OTDBRPC from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME -from lofar.sas.systemstatus.service.SSDBrpc import SSDBRPC -from lofar.sas.systemstatus.service.config import DEFAULT_SSDB_BUSNAME -from lofar.sas.systemstatus.service.config import DEFAULT_SSDB_SERVICENAME +from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_BUSNAME +from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_PREFIX + +from lofar.mom.momqueryservice.momqueryrpc import MoMQueryRPC +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +from lofar.sas.datamanagement.cleanup.rpc import CleanupRPC +from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME as DEFAULT_CLEANUP_BUSNAME +from lofar.sas.datamanagement.cleanup.config import DEFAULT_SERVICENAME as DEFAULT_CLEANUP_SERVICENAME logger = logging.getLogger(__name__) @@ -55,10 +65,14 @@ class ResourceAssigner(): radb_servicename=RADB_SERVICENAME, re_busname=RE_BUSNAME, re_servicename=RE_SERVICENAME, - ssdb_busname=DEFAULT_SSDB_BUSNAME, - ssdb_servicename=DEFAULT_SSDB_SERVICENAME, otdb_busname=DEFAULT_OTDB_SERVICE_BUSNAME, otdb_servicename=DEFAULT_OTDB_SERVICENAME, + cleanup_busname=DEFAULT_CLEANUP_BUSNAME, + cleanup_servicename=DEFAULT_CLEANUP_SERVICENAME, + ra_notification_busname=DEFAULT_RA_NOTIFICATION_BUSNAME, + ra_notification_prefix=DEFAULT_RA_NOTIFICATION_PREFIX, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, broker=None): """ ResourceAssigner inserts/updates tasks in the radb and assigns resources to it based on incoming parset. @@ -66,14 +80,15 @@ class ResourceAssigner(): :param radb_servicename: servicename of the radb service (default: RADBService) :param re_busname: busname on which the resource estimator service listens (default: lofar.ra.command) :param re_servicename: servicename of the resource estimator service (default: ResourceEstimation) - :param ssdb_busname: busname on which the ssdb service listens (default: lofar.system) - :param ssdb_servicename: servicename of the radb service (default: SSDBService) :param broker: Valid Qpid broker host (default: None, which means localhost) """ - self.radbrpc = RARPC(servicename=radb_servicename, busname=radb_busname, broker=broker) - self.rerpc = RPC(re_servicename, busname=re_busname, broker=broker, ForwardExceptions=True) - self.ssdbrpc = SSDBRPC(servicename=ssdb_servicename, busname=ssdb_busname, broker=broker) - self.otdbrpc = OTDBRPC(busname=otdb_busname, servicename=otdb_servicename, broker=broker) ## , ForwardExceptions=True hardcoded in RPCWrapper right now + self.radbrpc = RARPC(servicename=radb_servicename, busname=radb_busname, broker=broker, timeout=180) + self.rerpc = RPC(re_servicename, busname=re_busname, broker=broker, ForwardExceptions=True, timeout=180) + self.otdbrpc = OTDBRPC(busname=otdb_busname, servicename=otdb_servicename, broker=broker, timeout=180) ## , ForwardExceptions=True hardcoded in RPCWrapper right now + self.momrpc = MoMQueryRPC(servicename=mom_servicename, busname=mom_busname, broker=broker, timeout=180) + self.curpc = CleanupRPC(busname=cleanup_busname, servicename=cleanup_servicename, broker=broker) + self.ra_notification_bus = ToBus(address=ra_notification_busname, broker=broker) + self.ra_notification_prefix = ra_notification_prefix def __enter__(self): """Internal use only. (handles scope 'with')""" @@ -89,14 +104,18 @@ class ResourceAssigner(): self.radbrpc.open() self.rerpc.open() self.otdbrpc.open() - self.ssdbrpc.open() + self.momrpc.open() + self.curpc.open() + self.ra_notification_bus.open() def close(self): """Close rpc connections to radb service and resource estimator service""" self.radbrpc.close() self.rerpc.close() self.otdbrpc.close() - self.ssdbrpc.close() + self.momrpc.close() + self.curpc.close() + self.ra_notification_bus.close() def doAssignment(self, specification_tree): logger.info('doAssignment: specification_tree=%s' % (specification_tree)) @@ -112,17 +131,61 @@ class ResourceAssigner(): mainParset = parameterset(specification_tree['specification']) momId = mainParset.getInt('Observation.momID', -1) + + clusterIsCEP4 = self.checkClusterIsCEP4(mainParset) + clusterName = 'CEP4' if clusterIsCEP4 else 'CEP2' + + def applySaneStartEndTime(): + if clusterIsCEP4: + startTime = datetime.utcnow() + timedelta(minutes=1) + + maxPredecessorEndTime = self.getMaxPredecessorEndTime(specification_tree) + if maxPredecessorEndTime and maxPredecessorEndTime > startTime: + startTime = maxPredecessorEndTime + timedelta(minutes=1) + + taskDuration = mainParset.getInt('Observation.Scheduler.taskDuration', -1) + taskDuration = timedelta(seconds=taskDuration) if taskDuration > 0 else timedelta(hours=1) + + endTime = startTime + taskDuration + + logger.warning('Applying sane defaults (%s, %s) for start/end time from specification for otdb_id=%s', + startTime, endTime, otdb_id) + + try: + logger.info('uploading auto-generated start/end time (%s, %s) to otdb for otdb_id=%s', startTime, endTime, otdb_id) + self.otdbrpc.taskSetSpecification(otdb_id, { 'LOFAR.ObsSW.Observation.startTime': startTime.strftime('%Y-%m-%d %H:%M:%S'), + 'LOFAR.ObsSW.Observation.stopTime': endTime.strftime('%Y-%m-%d %H:%M:%S')}) + except Exception as e: + logger.error(e) + + return startTime, endTime + try: startTime = datetime.strptime(mainParset.getString('Observation.startTime'), '%Y-%m-%d %H:%M:%S') endTime = datetime.strptime(mainParset.getString('Observation.stopTime'), '%Y-%m-%d %H:%M:%S') + if startTime < datetime.utcnow(): + startTime, endTime = applySaneStartEndTime() except ValueError: - logger.warning('cannot parse for start/end time from specification for otdb_id=%s', (otdb_id, )) + logger.warning('cannot parse for start/end time from specification for otdb_id=%s. searching for sane defaults...', otdb_id) + startTime, endTime = applySaneStartEndTime() + + try: + # fix for MoM bug introduced before NV's holiday + # MoM sets ProcessingCluster.clusterName to CEP2 even when inputxml says CEP4 + # so, override it here if needed, and update to otdb + processingClusterName = mainParset.getString('Observation.Cluster.ProcessingCluster.clusterName', '') + if processingClusterName != clusterName: + logger.info('overwriting and uploading processingClusterName to otdb from %s to %s for otdb_id=%s', + processingClusterName, clusterName, otdb_id) + self.otdbrpc.taskSetSpecification(otdb_id, { 'LOFAR.ObsSW.Observation.Cluster.ProcessingCluster.clusterName': clusterName }) + except Exception as e: + logger.error(e) # insert new task and specification in the radb # any existing specification and task with same otdb_id will be deleted automatically - logger.info('doAssignment: insertSpecification momId=%s, otdb_id=%s, status=%s, taskType=%s, startTime=%s, endTime=%s' % - (momId, otdb_id, status, taskType, startTime, endTime)) - result = self.radbrpc.insertSpecificationAndTask(momId, otdb_id, status, taskType, startTime, endTime, str(mainParset)) + logger.info('doAssignment: insertSpecification momId=%s, otdb_id=%s, status=%s, taskType=%s, startTime=%s, endTime=%s cluster=%s' % + (momId, otdb_id, status, taskType, startTime, endTime, clusterName)) + result = self.radbrpc.insertSpecificationAndTask(momId, otdb_id, status, taskType, startTime, endTime, str(mainParset), clusterName) if not result['inserted']: logger.error('could not insert specification and task') @@ -132,16 +195,26 @@ class ResourceAssigner(): taskId = result['task_id'] logger.info('doAssignment: inserted specification (id=%s) and task (id=%s)' % (specificationId,taskId)) + task = self.radbrpc.getTask(taskId) + self.processPredecessors(task) + self.processSuccessors(task) + # do not assign resources to task for other clusters than cep4 - if not self.checkClusterIsCEP4(mainParset): + if not clusterIsCEP4: return if status != 'prescheduled': logger.info('skipping resource assignment for CEP4 task otdb_id=%s because status=%s' % (otdb_id, status)) return - needed = self.getNeededResouces(specification_tree) - logger.info('doAssignment: getNeededResouces=%s' % (needed,)) + try: + needed = self.getNeededResouces(specification_tree) + logger.info('doAssignment: getNeededResouces=%s' % (needed,)) + except Exception as e: + logger.error(e) + self.radbrpc.updateTask(taskId, status='error') + self._sendNotification(task, 'error') + return if not str(otdb_id) in needed: logger.error("no otdb_id %s found in estimator results %s" % (otdb_id, needed)) @@ -151,50 +224,129 @@ class ResourceAssigner(): logger.error("no task type %s found in estimator results %s" % (taskType, needed[str(otdb_id)])) return - # make sure the availability in the radb is up to date - # TODO: this should be updated regularly - try: - self.updateAvailableResources('cep4') - except Exception as e: - logger.warning("Exception while updating available resources: %s" % str(e)) - # claim the resources for this task # during the claim inserts the claims are automatically validated # and if not enough resources are available, then they are put to conflict status # also, if any claim is in conflict state, then the task is put to conflict status as well main_needed = needed[str(otdb_id)] - task = self.radbrpc.getTask(taskId) - claimed, claim_ids = self.claimResources(main_needed, task) - if claimed: - conflictingClaims = self.radbrpc.getResourceClaims(task_ids=taskId, status='conflict') - if conflictingClaims: - logger.warning('doAssignment: %s conflicting claims detected. Task cannot be scheduled. %s' % - (len(conflictingClaims), conflictingClaims)) + if 'errors' in main_needed and main_needed['errors']: + for error in main_needed['errors']: + logger.error("Error in estimator: %s", error) + + logger.error("Error(s) in estimator for otdb_id=%s radb_id=%s, setting task status to 'error'", otdb_id, taskId) + self.radbrpc.updateTask(taskId, status='error') + self._sendNotification(task, 'error') + else: + claimed, claim_ids = self.claimResources(main_needed, task) + if claimed: + conflictingClaims = self.radbrpc.getResourceClaims(task_ids=taskId, status='conflict') + + if conflictingClaims: + # radb set's task status to conflict automatically + logger.warning('doAssignment: %s conflicting claims detected. Task cannot be scheduled. %s' % + (len(conflictingClaims), conflictingClaims)) + self._sendNotification(task, 'conflict') + else: + logger.info('doAssignment: all claims for task %s were succesfully claimed. Setting claim statuses to allocated' % (taskId,)) + self.radbrpc.updateTaskAndResourceClaims(taskId, claim_status='allocated') + + # remove any output and/or intermediate data for restarting CEP4 pipelines + if task['type'] == 'pipeline': + path_result = self.curpc.getPathForOTDBId(task['otdb_id']) + if path_result['found']: + logger.info("removing data on disk from previous run for otdb_id %s", otdb_id) + result = self.curpc.removeTaskData(task['otdb_id']) + + if not result['deleted']: + logger.warning("could not remove all data on disk from previous run for otdb_id %s: %s", otdb_id, result['message']) + + # send notification that the task was scheduled, + # another service sets the parset spec in otdb, and updated otdb task status to scheduled, which is then synced to radb + self._sendNotification(task, 'scheduled') else: - logger.info('doAssignment: all claims for task %s were succesfully claimed. Setting task status to scheduled' % (taskId,)) - self.radbrpc.updateTaskAndResourceClaims(taskId, task_status='scheduled', claim_status='allocated') - - self.processPredecessors(specification_tree) + if claim_ids: + logger.warning('doAssignment: Not all claims could be inserted. Setting task %s status to conflict' % (taskId)) + self.radbrpc.updateTask(taskId, status='conflict') + self._sendNotification(task, 'conflict') + else: + logger.warning('doAssignment: No claims could be made. Setting task %s status to error' % (taskId)) + self.radbrpc.updateTask(taskId, status='error') + self._sendNotification(task, 'error') + + def _sendNotification(self, task, status): + try: + if status == 'scheduled' or status == 'conflict' or status == 'error': + content={'radb_id': task['id'], 'otdb_id':task['otdb_id'], 'mom_id': task['mom_id']} + subject= 'Task' + status[0].upper() + status[1:] + msg = EventMessage(context=self.ra_notification_prefix + subject, content=content) + logger.info('Sending notification %s: %s' % (subject, str(content).replace('\n', ' '))) + self.ra_notification_bus.send(msg) + except Exception as e: + logger.error(str(e)) - def processPredecessors(self, specification_tree): + def processPredecessors(self, task): try: - predecessor_trees = specification_tree['predecessors'] + predecessor_mom_ids = self.momrpc.getPredecessorIds(task['mom_id'])[str(task['mom_id'])] - if predecessor_trees: - otdb_id = specification_tree['otdb_id'] - task = self.radbrpc.getTask(otdb_id=otdb_id) + if predecessor_mom_ids: + logger.info('proccessing predecessor mom_ids=%s for mom_id=%s otdb_id=%s', predecessor_mom_ids, task['mom_id'], task['otdb_id']) - for predecessor_tree in predecessor_trees: - pred_otdb_id = predecessor_tree['otdb_id'] - predecessor_task = self.radbrpc.getTask(otdb_id=pred_otdb_id) + for predecessor_mom_id in predecessor_mom_ids: + #check if the predecessor needs to be linked to this task + predecessor_task = self.radbrpc.getTask(mom_id=predecessor_mom_id) if predecessor_task: - self.radbrpc.insertTaskPredecessor(task['id'], predecessor_task['id']) - self.processPredecessors(predecessor_tree) + if predecessor_task['id'] not in task['predecessor_ids']: + logger.info('connecting predecessor task with mom_id=%s otdb_id=%s to it\'s successor with mom_id=%s otdb_id=%s', predecessor_task['mom_id'], + predecessor_task['otdb_id'], + task['mom_id'], + task['otdb_id']) + self.radbrpc.insertTaskPredecessor(task['id'], predecessor_task['id']) + else: + logger.warning('could not find predecessor task with otdb_id=%s in radb for task otdb_id=%s', predecessor_task['otdb_id'], task['otdb_id']) + else: + logger.info('no predecessors for otdb_id=%s', task['otdb_id']) except Exception as e: logger.error(e) + def processSuccessors(self, task): + try: + successor_mom_ids = self.momrpc.getSuccessorIds(task['mom_id'])[str(task['mom_id'])] + + if successor_mom_ids: + logger.info('proccessing successor mom_ids=%s for mom_id=%s otdb_id=%s', successor_mom_ids, task['mom_id'], task['otdb_id']) + + for successor_mom_id in successor_mom_ids: + #check if the successor needs to be linked to this task + successor_task = self.radbrpc.getTask(mom_id=successor_mom_id) + if successor_task: + if successor_task['id'] not in task['successor_ids']: + logger.info('connecting successor task with mom_id=%s otdb_id=%s to it\'s predecessor with mom_id=%s otdb_id=%s', successor_task['mom_id'], + successor_task['otdb_id'], + task['mom_id'], + task['otdb_id']) + self.radbrpc.insertTaskPredecessor(successor_task['id'], task['id']) + movePipelineAfterItsPredecessors(successor_task, self.radbrpc) + else: + logger.warning('could not find predecessor task with otdb_id=%s in radb for task otdb_id=%s', successor_task['otdb_id'], task['otdb_id']) + else: + logger.info('no successors for otdb_id=%s', task['otdb_id']) + + except Exception as e: + logger.error(e) + + def getMaxPredecessorEndTime(self, specification_tree): + try: + predecessor_specs = [parameterset(tree['specification']) for tree in specification_tree['predecessors']] + predecessor_endTimes = [datetime.strptime(spec.getString('Observation.stopTime'), '%Y-%m-%d %H:%M:%S') for spec in predecessor_specs] + + if predecessor_endTimes: + return max(predecessor_endTimes) + except Exception as e: + logger.error(e) + return None + def checkClusterIsCEP4(self, parset): # check storageClusterName for enabled DataProducts # if any storageClusterName is not CEP4, we do not accept this parset @@ -219,34 +371,6 @@ class ResourceAssigner(): logger.info('getNeededResouces: %s' % replymessage) return replymessage - def updateAvailableResources(self, cluster): - # find out which resources are available - # and what is their capacity - # For now, only look at CEP4 storage - # Later, also look at stations up/down for short term scheduling - - #get all active groupnames, find id for cluster group - groupnames = self.ssdbrpc.getactivegroupnames() - cluster_group_id = next(k for k,v in groupnames.items() if v == cluster) - - # for CEP4 cluster, do hard codes lookup of first and only node - node_info = self.ssdbrpc.gethostsforgid(cluster_group_id)['nodes'][0] - - storage_resources = self.radbrpc.getResources(resource_types='storage', include_availability=True) - cep4_storage_resource = next(x for x in storage_resources if 'cep4' in x['name']) - active = node_info['statename'] == 'Active' - total_capacity = node_info['totalspace'] - available_capacity = total_capacity - node_info['usedspace'] - - logger.info("Updating resource availability of %s (id=%s) to active=%s available_capacity=%s total_capacity=%s" % - (cep4_storage_resource['name'], cep4_storage_resource['id'], active, available_capacity, total_capacity)) - - self.radbrpc.updateResourceAvailability(cep4_storage_resource['id'], - active=active, - available_capacity=available_capacity, - total_capacity=total_capacity) - - def claimResources(self, needed_resources, task): logger.info('claimResources: task %s needed_resources=%s' % (task, needed_resources)) @@ -263,7 +387,7 @@ class ResourceAssigner(): claims = [] for resource_type_name, needed_claim_for_resource_type in needed_resources_for_task_type.items(): if resource_type_name in resource_types: - logger.info('claimResources: processing resource_type: %s' % resource_type_name) + logger.info('claimResources: processing resource_type: %s contents: %s' % (resource_type_name, needed_claim_for_resource_type)) db_resource_type_id = resource_types[resource_type_name] db_resources_for_type = [r for r in resources if r['type_id'] == db_resource_type_id] @@ -282,33 +406,41 @@ class ResourceAssigner(): 'status':'claimed', 'claim_size':needed_claim_value} - #FIXME: find proper way to extend storage time with a month + # FIXME: find proper way to extend storage time with a year + # 2016-09-27 scisup would like to be involved in chosing these kind of defaults + # and what to do after the claim expires + # we now choose a default period of a year, and do nothing if the claim expires if 'storage' in db_cep4_resources_for_type[0]['name']: - claim['endtime'] += timedelta(days=31) + claim['endtime'] += timedelta(days=365) # if the needed_claim_for_resource_type dict contains more kvp's, - # then the subdict contains groups of properties for the claim + # then the subdicts contains groups of properties for the claim if len(needed_claim_for_resource_type) > 1: claim['properties'] = [] - needed_prop_groups = next((v for k,v in needed_claim_for_resource_type.items() if isinstance(v, collections.Iterable))) - def processProperties(propertiesDict, sap_nr=None): + def processProperties(propertiesDict, sap_nr=None, is_input=False): for prop_type_name, prop_value in propertiesDict.items(): if prop_type_name in rc_property_types: rc_property_type_id = rc_property_types[prop_type_name] - property = {'type':rc_property_type_id, 'value':prop_value} + property = {'type':rc_property_type_id, + 'value':prop_value, + 'io_type': 'input' if is_input else 'output'} if sap_nr is not None: property['sap_nr'] = sap_nr claim['properties'].append(property) else: logger.error('claimResources: unknown prop_type:%s' % prop_type_name) - for group_name, needed_prop_group in needed_prop_groups.items(): - if group_name == 'saps': - for sap_dict in needed_prop_group: - processProperties(sap_dict['properties'], sap_dict['sap_nr']) - else: - processProperties(needed_prop_group) + subdicts = {k:v for k,v in needed_claim_for_resource_type.items() if isinstance(v, dict)} + for subdict_name, subdict in subdicts.items(): + logger.info('claimResources: processing resource_type: %s subdict_name: \'%s\' subdict_contents: %s' % (resource_type_name, subdict_name, subdict)) + is_input = 'input' in subdict_name.lower() + for group_name, needed_prop_group in subdict.items(): + if group_name == 'saps': + for sap_dict in needed_prop_group: + processProperties(sap_dict['properties'], sap_dict['sap_nr'], is_input) + else: + processProperties(needed_prop_group, None, is_input) logger.info('claimResources: created claim:%s' % claim) claims.append(claim) @@ -318,5 +450,4 @@ class ResourceAssigner(): logger.info('claimResources: inserting %d claims in the radb' % len(claims)) claim_ids = self.radbrpc.insertResourceClaims(task['id'], claims, 1, 'anonymous', -1)['ids'] logger.info('claimResources: %d claims were inserted in the radb' % len(claim_ids)) - return len(claim_ids) == len(claims), claim_ids - + return (len(claim_ids) > 0 and len(claim_ids) == len(claims)), claim_ids diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/config.py b/SAS/ResourceAssignment/ResourceAssigner/lib/config.py index 24a16cad21b25421a2e0bfc042b22ecd1821ed03..761d48d09b100638d059c9e5dbc82f7d818e9502 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/config.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/config.py @@ -5,3 +5,9 @@ from lofar.messaging import adaptNameToEnvironment DEFAULT_BUSNAME = adaptNameToEnvironment('lofar.ra.command') DEFAULT_SERVICENAME = 'RAService' + +DEFAULT_RA_NOTIFICATION_BUSNAME = adaptNameToEnvironment('lofar.ra.notification') +DEFAULT_RA_NOTIFICATION_PREFIX = 'ResourceAssigner.' +DEFAULT_RA_NOTIFICATION_SUBJECTS=DEFAULT_RA_NOTIFICATION_PREFIX+'*' + +PIPELINE_CHECK_INTERVAL=300 diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/rabuslistener.py b/SAS/ResourceAssignment/ResourceAssigner/lib/rabuslistener.py new file mode 100644 index 0000000000000000000000000000000000000000..bbeec2501294045f96587ce043fdb7ec67768db6 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/rabuslistener.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +# RABusListener.py +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id: RABusListener.py 1580 2015-09-30 14:18:57Z loose $ + +""" +RABusListener listens on the lofar notification message bus and calls (empty) on<SomeMessage> methods when such a message is received. +Typical usage is to derive your own subclass from RABusListener and implement the specific on<SomeMessage> methods that you are interested in. +""" + +from lofar.messaging.messagebus import AbstractBusListener +from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_BUSNAME, DEFAULT_RA_NOTIFICATION_SUBJECTS +from lofar.common.util import waitForInterrupt + +import qpid.messaging +import logging +from datetime import datetime + +logger = logging.getLogger(__name__) + + +class RABusListener(AbstractBusListener): + def __init__(self, busname=DEFAULT_RA_NOTIFICATION_BUSNAME, subjects=DEFAULT_RA_NOTIFICATION_SUBJECTS, broker=None, **kwargs): + """ + RABusListener listens on the lofar notification message bus and calls (empty) on<SomeMessage> methods when such a message is received. + Typical usage is to derive your own subclass from RABusListener and implement the specific on<SomeMessage> methods that you are interested in. + :param busname: valid Qpid address (default: lofar.ra.notification) + :param broker: valid Qpid broker host (default: None, which means localhost) + additional parameters in kwargs: + options= <dict> Dictionary of options passed to QPID + exclusive= <bool> Create an exclusive binding so no other services can consume duplicate messages (default: False) + numthreads= <int> Number of parallel threads processing messages (default: 1) + verbose= <bool> Output extra logging over stdout (default: False) + """ + self.subject_prefix = (subjects.split('.')[0]+'.') if '.' in subjects else '' + + address = "%s/%s" % (busname, subjects) + super(RABusListener, self).__init__(address, broker, **kwargs) + + + def _handleMessage(self, msg): + logger.info("on%s: %s" % (msg.subject.replace(self.subject_prefix, ''), str(msg.content).replace('\n', ' '))) + + if msg.subject == '%sTaskScheduled' % self.subject_prefix: + self.onTaskScheduled(msg.content) + elif msg.subject == '%sTaskConflict' % self.subject_prefix: + self.onTaskConflict(msg.content) + elif msg.subject == '%sTaskError' % self.subject_prefix: + self.onTaskError(msg.content) + else: + logger.error("RABusListener.handleMessage: unknown subject: %s" %str(msg.subject)) + + def onTaskScheduled(self, task_ids): + '''onTaskScheduled is called upon receiving a TaskScheduled message. + :param task_ids: a dict containing radb_id, mom_id and otdb_id''' + pass + + def onTaskConflict(self, task_ids): + '''onTaskConflict is called upon receiving a TaskConflict message. + :param task_ids: a dict containing radb_id, mom_id and otdb_id''' + pass + + def onTaskError(self, task_ids): + '''onTaskError is called upon receiving a TaskError message. + :param task_ids: a dict containing radb_id, mom_id and otdb_id''' + pass + +if __name__ == '__main__': + with RABusListener(broker=None) as listener: + waitForInterrupt() + +__all__ = ["RABusListener"] diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py b/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py index 4d7e7c0e14f815650b67a01d80598fcc72ddd03c..10fa2a01a1a083c5b1f0e7493c9653bb80d85db0 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py @@ -39,6 +39,7 @@ import lofar.sas.resourceassignment.resourceassignmentservice.rpc as rarpc from lofar.sas.resourceassignment.rataskspecified.config import DEFAULT_RA_TASK_SPECIFIED_NOTIFICATION_BUSNAME from lofar.sas.resourceassignment.rataskspecified.config import DEFAULT_RA_TASK_SPECIFIED_NOTIFICATION_SUBJECT from lofar.sas.resourceassignment.resourceassigner.assignment import ResourceAssigner +from lofar.sas.resourceassignment.resourceassigner.schedulechecker import ScheduleChecker logger = logging.getLogger(__name__) @@ -73,6 +74,10 @@ class SpecifiedTaskListener(RATaskSpecifiedBusListener): __all__ = ["SpecifiedTaskListener"] def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + from optparse import OptionParser from lofar.messaging import setQpidLogLevel from lofar.common.util import waitForInterrupt @@ -82,8 +87,11 @@ def main(): from lofar.sas.resourceassignment.resourceassignmentestimator.config import DEFAULT_BUSNAME as RE_BUSNAME from lofar.sas.resourceassignment.resourceassignmentestimator.config import DEFAULT_SERVICENAME as RE_SERVICENAME from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME - from lofar.sas.systemstatus.service.config import DEFAULT_SSDB_BUSNAME - from lofar.sas.systemstatus.service.config import DEFAULT_SSDB_SERVICENAME + from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_BUSNAME + from lofar.sas.resourceassignment.resourceassigner.config import DEFAULT_RA_NOTIFICATION_PREFIX + from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME as DEFAULT_CLEANUP_BUSNAME + from lofar.sas.datamanagement.cleanup.config import DEFAULT_SERVICENAME as DEFAULT_CLEANUP_SERVICENAME # Check the invocation arguments parser = OptionParser("%prog [options]", @@ -111,16 +119,23 @@ def main(): help="Name of the resource estimator service. [default: %default]") parser.add_option("--otdb_busname", dest="otdb_busname", type="string", default=DEFAULT_OTDB_SERVICE_BUSNAME, help="Name of the bus on which the OTDB service listens, default: %default") parser.add_option("--otdb_servicename", dest="otdb_servicename", type="string", default=DEFAULT_OTDB_SERVICENAME, help="Name of the OTDB service, default: %default") - parser.add_option("--ssdb_busname", dest="ssdb_busname", type="string", - default=DEFAULT_SSDB_BUSNAME, - help="Name of the bus on which the ssdb service listens. [default: %default]") - parser.add_option("--ssdb_servicename", dest="ssdb_servicename", type="string", - default=DEFAULT_SSDB_SERVICENAME, - help="Name of the ssdb service. [default: %default]") + parser.add_option('--cleanup_busname', dest='cleanup_busname', type='string', default=DEFAULT_CLEANUP_BUSNAME, help='Name of the bus exchange on the qpid broker on which the cleanupservice listens, default: %default') + parser.add_option('--cleanup_servicename', dest='cleanup_servicename', type='string', default=DEFAULT_CLEANUP_SERVICENAME, help='Name of the cleanupservice, default: %default') + parser.add_option("--ra_notification_busname", dest="ra_notification_busname", type="string", + default=DEFAULT_RA_NOTIFICATION_BUSNAME, + help="Name of the notification bus on which the resourceassigner publishes its notifications. [default: %default]") + parser.add_option("--ra_notification_prefix", dest="ra_notification_prefix", type="string", + default=DEFAULT_RA_NOTIFICATION_PREFIX, + help="Prefix for the subject of the by the resourceassigner published notification messages. [default: %default]") + parser.add_option('--mom_query_busname', dest='mom_query_busname', type='string', + default=DEFAULT_MOMQUERY_BUSNAME, + help='Name of the bus exchange on the qpid broker on which the momqueryservice listens, default: %default') + parser.add_option('--mom_query_servicename', dest='mom_query_servicename', type='string', + default=DEFAULT_MOMQUERY_SERVICENAME, + help='Name of the momqueryservice, default: %default') parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') (options, args) = parser.parse_args() - logging.getLogger('lofar.messaging.messagebus').setLevel(logging.WARNING) setQpidLogLevel(logging.INFO) logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG if options.verbose else logging.INFO) @@ -131,14 +146,23 @@ def main(): re_servicename=options.re_servicename, otdb_busname=options.otdb_busname, otdb_servicename=options.otdb_servicename, - ssdb_busname=options.ssdb_busname, - ssdb_servicename=options.ssdb_servicename, + cleanup_busname=options.cleanup_busname, + cleanup_servicename=options.cleanup_servicename, + ra_notification_busname=options.ra_notification_busname, + ra_notification_prefix=options.ra_notification_prefix, + mom_busname=options.mom_query_busname, + mom_servicename=options.mom_query_servicename, broker=options.broker) as assigner: with SpecifiedTaskListener(busname=options.notification_busname, subject=options.notification_subject, broker=options.broker, assigner=assigner) as listener: - waitForInterrupt() + with ScheduleChecker(radb_busname=options.radb_busname, + radb_servicename=options.radb_servicename, + mom_busname=options.mom_query_busname, + mom_servicename=options.mom_query_servicename, + broker=options.broker) as schedulechecker: + waitForInterrupt() if __name__ == '__main__': main() diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py new file mode 100644 index 0000000000000000000000000000000000000000..fa025466cbcad20dcaa36aa1acedf56835e6d2f5 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# + +import qpid.messaging +import logging +from datetime import datetime, timedelta +from time import sleep +from threading import Thread + +from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as DEFAULT_RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RADB_SERVICENAME + +from lofar.mom.momqueryservice.momqueryrpc import MoMQueryRPC +from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME + +from lofar.sas.resourceassignment.resourceassigner.config import PIPELINE_CHECK_INTERVAL + +logger = logging.getLogger(__name__) + +def movePipelineAfterItsPredecessors(task, radbrpc, min_start_timestamp=None): + try: + #only reschedule pipelines which run on cep4 + if task and task['type'] == 'pipeline' and task.get('cluster') == 'CEP4': + logger.info("checking pipeline starttime radb_id=%s otdb_id=%s", task['id'], task['otdb_id']) + + predecessor_tasks = radbrpc.getTasks(task_ids=task['predecessor_ids']) + + predecessor_endtimes = [t['endtime'] for t in predecessor_tasks] + if min_start_timestamp: + predecessor_endtimes.append(min_start_timestamp) + + max_pred_endtime = max(predecessor_endtimes) + + if (task['starttime'] < max_pred_endtime) or (min_start_timestamp and task['starttime'] > min_start_timestamp): + shift = max_pred_endtime - task['starttime'] + newStartTime = task['starttime']+shift + newEndTime = task['endtime']+shift + + # move pipeline even further ahead in case there are more than 2 overlapping scheduled/queued pipelines + while True: + overlapping_pipelines = radbrpc.getTasks(lower_bound=newStartTime, upper_bound=newEndTime, task_type='pipeline', task_status=['scheduled', 'queued', 'active', 'completing'], cluster='CEP4') + #exclude self + overlapping_pipelines = [pl for pl in overlapping_pipelines if pl['id'] != task['id']] + + if len(overlapping_pipelines) >= 2: + max_overlapping_pipeline_endtime = max([t['endtime'] for t in overlapping_pipelines]) + shift = max_overlapping_pipeline_endtime + timedelta(minutes=1) - task['starttime'] + newStartTime = task['starttime']+shift + newEndTime = task['endtime']+shift + else: + break + + if shift != timedelta(seconds=0): + logger.info("Moving %s pipeline radb_id=%s otdb_id=%s by %s from \'%s\' to \'%s\'", task['status'], task['id'], task['otdb_id'], shift, task['starttime'], newStartTime) + radbrpc.updateTaskAndResourceClaims(task['id'], starttime=newStartTime, endtime=newEndTime) + updated_task = radbrpc.getTask(task['id']) + if updated_task['status'] != task['status']: + logger.warn("Moving of pipeline radb_id=%s otdb_id=%s caused the status to change from %s to %s", updated_task['id'], updated_task['otdb_id'], task['status'], updated_task['status']) + #TODO: automatically resolve conflict status by moved pipeline in first free time slot. + except Exception as e: + logger.error("Error while checking pipeline starttime: %s", e) + +class ScheduleChecker(): + def __init__(self, + radb_busname=DEFAULT_RADB_BUSNAME, + radb_servicename=DEFAULT_RADB_SERVICENAME, + mom_busname=DEFAULT_MOMQUERY_BUSNAME, + mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, + broker=None): + """ + """ + self._thread = None + self._running = False + self._radbrpc = RARPC(servicename=radb_servicename, busname=radb_busname, broker=broker) + self._momrpc = MoMQueryRPC(servicename=mom_servicename, busname=mom_busname, broker=broker) + + def __enter__(self): + """Internal use only. (handles scope 'with')""" + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Internal use only. (handles scope 'with')""" + self.stop() + + def start(self): + """Open rpc connections to radb service and resource estimator service""" + self._radbrpc.open() + self._momrpc.open() + self._running = True + self._thread = Thread(target=self._check_loop) + self._thread.daemon = True + self._thread.start() + + def stop(self): + """Close rpc connections to radb service and resource estimator service""" + self._radbrpc.close() + self._momrpc.close() + self._running = False + self._thread.join(60) + + + def checkRunningPipelines(self): + try: + now = datetime.utcnow() + + active_pipelines = self._radbrpc.getTasks(task_status='active', task_type='pipeline') + + if active_pipelines: + logger.info('checking endtime of running pipelines') + + for task in active_pipelines: + if task['endtime'] <= now: + new_endtime=now+timedelta(seconds=PIPELINE_CHECK_INTERVAL) + logger.info("Extending endtime to %s for pipeline radb_id=%s otdb_id=%s", new_endtime, task['id'], task['otdb_id']) + self._radbrpc.updateTaskAndResourceClaims(task['id'], endtime=new_endtime) + except Exception as e: + logger.error("Error while checking running pipelines: %s", e) + + def checkScheduledAndQueuedPipelines(self): + try: + now = datetime.utcnow() + min_start_timestamp = now + timedelta(seconds=PIPELINE_CHECK_INTERVAL) + + pipelines = self._radbrpc.getTasks(task_status=['scheduled', 'queued'], task_type='pipeline', cluster='CEP4') + + if pipelines: + logger.info('checking starttime of %s scheduled/queued cep4 pipelines min_start_timestamp=%s', len(pipelines), min_start_timestamp) + pipelines.sort(key=lambda pl: pl['starttime'], reverse=True) + + for task in pipelines: + # moving pipelines might take a while + # so this task might have changed status to active + # in that case we don't want to move it + uptodate_task = self._radbrpc.getTask(task['id']) + if uptodate_task['status'] in ['scheduled', 'queued']: + movePipelineAfterItsPredecessors(uptodate_task, self._radbrpc, min_start_timestamp) + except Exception as e: + logger.error("Error while checking scheduled pipelines: %s", e) + + def checkApprovedTasks(self): + try: + logger.info('checking approved tasks for status in mom') + approved_tasks = self._radbrpc.getTasks(task_status='approved') + mom_ids = [t['mom_id'] for t in approved_tasks] + mom_details = self._momrpc.getProjectDetails(mom_ids) + + for task in approved_tasks: + if (str(task['mom_id']) not in mom_details or + mom_details[str(task['mom_id'])]['object_status'] == 'opened'): + logger.info('task %s mom_id=%s otdb_id=%s was removed or set to status opened. removing task from rabd', task['id'], task['mom_id'], task['otdb_id']) + self._radbrpc.deleteSpecification(task['specification_id']) + + except Exception as e: + logger.error("Error while checking scheduled pipelines: %s", e) + + def _check_loop(self): + while self._running: + self.checkRunningPipelines() + self.checkScheduledAndQueuedPipelines() + self.checkApprovedTasks() + + for i in range(PIPELINE_CHECK_INTERVAL): + sleep(1) + + if not self._running: + break diff --git a/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py b/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py index 76848599e5472d646fbbc9db474d77f678327e38..5c8c6209c4f9dab8d4b06e9ab2c05d16ee9c5e4d 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py +++ b/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py @@ -47,10 +47,6 @@ with patch('lofar.sas.resourceassignment.resourceassignmentservice.rpc.RARPC', a #give pre-cooked answer depending on called service if servicename == 'ResourceEstimation': return {'1290472': {'observation': {'bandwidth': {'total_size': 9372800}, 'storage': {'total_size': 140592000, 'output_files': {'is': {'is_nr_stokes': 1, 'is_file_size': 36864000, 'nr_of_is_files': 1}, 'uv': {'nr_of_uv_files': 50, 'uv_file_size': 2074560}, 'saps': [{'sap_nr': 0, 'properties': {'nr_of_uv_files': 50, 'nr_of_is_files': 1}}]}}}}}, "OK" - elif servicename == 'SSDBService.GetActiveGroupNames': - return {0:'storagenodes', 1:'computenodes', 2:'archivenodes', 3:'locusnodes', 4:'cep4'}, "OK" - elif servicename == 'SSDBService.GetHostForGID': - return {u'groupname': u'cep4', u'nodes': [{u'claimedspace': 0, u'totalspace': 702716, u'statename': u'Active', u'usedspace': 23084, u'id': 1, u'groupname': u'cep4', u'path': u'/lustre', u'hostname': u'lustre001'}]}, "OK" return None, None @@ -59,6 +55,15 @@ with patch('lofar.sas.resourceassignment.resourceassignmentservice.rpc.RARPC', a # import ResourceAssigner now, so it will use the mocked classes and methods from lofar.sas.resourceassignment.resourceassigner.assignment import ResourceAssigner + try: + with ResourceAssigner() as assigner: + pass + except Exception as e: + if 'NotFound: no such queue' in e.message: + print e.message + print 'qpid environment not set up correctly for this test' + exit(3) + #define the test class class ResourceAssignerTest(unittest.TestCase): '''Test the logic in the ResourceAssigner''' @@ -85,4 +90,4 @@ with patch('lofar.sas.resourceassignment.resourceassignmentservice.rpc.RARPC', a #TODO: added test asserts etc - unittest.main(verbosity=2) + unittest.main() diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py index e1d0196bafaf7e7d744692f14f46a40d548e4b25..14a18dea7ed4dae61d8dece5aed249b6a2467857 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py @@ -45,6 +45,8 @@ class RADatabase: self.conn = None self.cursor = None self.log_queries = log_queries + self._taskStatusCache = {} + self._taskTypeCache = {} def _connect(self): self.conn = None @@ -109,12 +111,16 @@ class RADatabase: def getTaskStatusNames(self): return [x['name'] for x in self.getTaskStatuses()] - def getTaskStatusId(self, status_name): + def getTaskStatusId(self, status_name, from_cache=False): + if from_cache and status_name in self._taskStatusCache: + return self._taskStatusCache[status_name] + query = '''SELECT id from resource_allocation.task_status WHERE name = %s;''' result = self._executeQuery(query, [status_name], fetch=_FETCH_ONE) if result: + self._taskStatusCache[status_name] = result['id'] return result['id'] raise KeyError('No such status: %s Valid values are: %s' % (status_name, ', '.join(self.getTaskStatusNames()))) @@ -127,12 +133,16 @@ class RADatabase: def getTaskTypeNames(self): return [x['name'] for x in self.getTaskTypes()] - def getTaskTypeId(self, type_name): + def getTaskTypeId(self, type_name, from_cache=False): + if from_cache and type_name in self._taskStatusCache: + return self._taskTypeCache[type_name] + query = '''SELECT id from resource_allocation.task_type WHERE name = %s;''' result = self._executeQuery(query, [type_name], fetch=_FETCH_ONE) if result: + self._taskTypeCache[type_name] = result['id'] return result['id'] raise KeyError('No such type: %s Valid values are: %s' % (type_name, ', '.join(self.getTaskTypeNames()))) @@ -155,7 +165,56 @@ class RADatabase: raise KeyError('No such status: %s. Valid values are: %s' % (status_name, ', '.join(self.getResourceClaimStatusNames()))) - def getTasks(self, lower_bound=None, upper_bound=None): + def getTasksTimeWindow(self, task_ids=None, mom_ids=None, otdb_ids=None): + if len([x for x in [task_ids, mom_ids, otdb_ids] if x != None]) > 1: + raise KeyError("Provide either task_ids or mom_ids or otdb_ids, not multiple kinds.") + + query = '''SELECT min(starttime) as min_starttime, max(endtime) as max_endtime from resource_allocation.task_view''' + + conditions = [] + qargs = [] + + if task_ids is not None: + if isinstance(task_ids, int): # just a single id + conditions.append('id = %s') + qargs.append(task_ids) + elif len(task_ids) > 0: #assume a list/enumerable of id's + conditions.append('id in %s') + qargs.append(tuple(task_ids)) + elif len(task_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if mom_ids is not None: + if isinstance(mom_ids, int): # just a single id + conditions.append('mom_id = %s') + qargs.append(mom_ids) + elif len(mom_ids) > 0: #assume a list/enumerable of id's + conditions.append('mom_id in %s') + qargs.append(tuple(mom_ids)) + elif len(mom_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if otdb_ids is not None: + if isinstance(otdb_ids, int): # just a single id + conditions.append('otdb_id = %s') + qargs.append(otdb_ids) + elif len(otdb_ids) > 0: #assume a list/enumerable of id's + conditions.append('otdb_id in %s') + qargs.append(tuple(otdb_ids)) + elif len(otdb_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if conditions: + query += ' WHERE ' + ' AND '.join(conditions) + + result = list(self._executeQuery(query, qargs, fetch=_FETCH_ALL)) + + return result + + def getTasks(self, lower_bound=None, upper_bound=None, task_ids=None, task_status=None, task_type=None, mom_ids=None, otdb_ids=None, cluster=None): + if len([x for x in [task_ids, mom_ids, otdb_ids] if x != None]) > 1: + raise KeyError("Provide either task_ids or mom_ids or otdb_ids, not multiple kinds.") + query = '''SELECT * from resource_allocation.task_view''' conditions = [] @@ -169,58 +228,139 @@ class RADatabase: conditions.append('starttime <= %s') qargs.append(upper_bound) + if task_ids is not None: + if isinstance(task_ids, int): # just a single id + conditions.append('id = %s') + qargs.append(task_ids) + elif len(task_ids) > 0: #assume a list/enumerable of id's + conditions.append('id in %s') + qargs.append(tuple(task_ids)) + elif len(task_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if mom_ids is not None: + if isinstance(mom_ids, int): # just a single id + conditions.append('mom_id = %s') + qargs.append(mom_ids) + elif len(mom_ids) > 0: #assume a list/enumerable of id's + conditions.append('mom_id in %s') + qargs.append(tuple(mom_ids)) + elif len(mom_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if otdb_ids is not None: + if isinstance(otdb_ids, int): # just a single id + conditions.append('otdb_id = %s') + qargs.append(otdb_ids) + elif len(otdb_ids) > 0: #assume a list/enumerable of id's + conditions.append('otdb_id in %s') + qargs.append(tuple(otdb_ids)) + elif len(otdb_ids) == 0: #assume a list/enumerable of id's, length 0 + return [] + + task_status, task_type = self._convertTaskTypeAndStatusToIds(task_status, task_type) + + if task_status is not None: + if isinstance(task_status, int): # just a single id + conditions.append('status_id = %s') + qargs.append(task_status) + elif len(task_status) > 0: #assume a list/enumerable of id's + conditions.append('status_id in %s') + qargs.append(tuple(task_status)) + elif len(task_status) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if task_type is not None: + if isinstance(task_type, int): # just a single id + conditions.append('type_id = %s') + qargs.append(task_type) + elif len(task_type) > 0: #assume a list/enumerable of id's + conditions.append('type_id in %s') + qargs.append(tuple(task_type)) + elif len(task_type) == 0: #assume a list/enumerable of id's, length 0 + return [] + + if cluster is not None: + conditions.append('cluster = %s') + qargs.append(cluster) + if conditions: query += ' WHERE ' + ' AND '.join(conditions) tasks = list(self._executeQuery(query, qargs, fetch=_FETCH_ALL)) - predIds = self.getTaskPredecessorIds() - succIds = self.getTaskSuccessorIds() for task in tasks: - task['predecessor_ids'] = predIds.get(task['id'], []) - task['successor_ids'] = succIds.get(task['id'], []) - task['duration'] = totalSeconds(task['endtime'] - task['starttime']) + if task['predecessor_ids'] is None: + task['predecessor_ids'] = [] + + if task['successor_ids'] is None: + task['successor_ids'] = [] return tasks - def getTask(self, id=None, mom_id=None, otdb_id=None): - '''get a task for either the given (task)id, or for the given mom_id, or for the given otdb_id''' - ids = [id, mom_id, otdb_id] + def getTask(self, id=None, mom_id=None, otdb_id=None, specification_id=None): + '''get a task for either the given (task)id, or for the given mom_id, or for the given otdb_id, or for the given specification_id''' + ids = [id, mom_id, otdb_id, specification_id] validIds = [x for x in ids if x != None] if len(validIds) != 1: - raise KeyError("Provide one and only one id: id=%s, mom_id=%s, otdb_id=%s" % (id, mom_id, otdb_id)) + raise KeyError("Provide one and only one id: id=%s, mom_id=%s, otdb_id=%s, specification_id=%s" % (id, mom_id, otdb_id, specification_id)) query = '''SELECT * from resource_allocation.task_view tv ''' - if id: + if id is not None: query += '''where tv.id = (%s);''' - elif mom_id: + elif mom_id is not None: query += '''where tv.mom_id = (%s);''' - elif otdb_id: + elif otdb_id is not None: query += '''where tv.otdb_id = (%s);''' + elif specification_id is not None: + query += '''where tv.specification_id = (%s);''' + result = self._executeQuery(query, validIds, fetch=_FETCH_ONE) task = dict(result) if result else None if task: - task['duration'] = totalSeconds(task['endtime'] - task['starttime']) + if task['predecessor_ids'] is None: + task['predecessor_ids'] = [] + + if task['successor_ids'] is None: + task['successor_ids'] = [] return task + def _convertTaskStatusToId(self, task_status): + '''converts task_status to id in case it is a string or list of strings''' + if task_status is not None: + if isinstance(task_status, basestring): + return self.getTaskStatusId(task_status, True) + else: #assume iterable + return [self._convertTaskStatusToId(x) for x in task_status] + + return task_status + + def _convertTaskTypeToId(self, task_type): + '''converts task_status to id in case it is a string or list of strings''' + if task_type is not None: + if isinstance(task_type, basestring): + return self.getTaskTypeId(task_type, True) + else: #assume iterable + return [self._convertTaskTypeToId(x) for x in task_type] + + return task_type + def _convertTaskTypeAndStatusToIds(self, task_status, task_type): '''converts task_status and task_type to id's in case one and/or the other are strings''' - if task_status and isinstance(task_status, basestring): - #convert task_status string to task_status.id - task_status = self.getTaskStatusId(task_status) + return self._convertTaskStatusToId(task_status), self._convertTaskTypeToId(task_type) - if task_type and isinstance(task_type, basestring): - #convert task_type string to task_type.id - task_type = self.getTaskTypeId(task_type) + def insertTask(self, mom_id, otdb_id, task_status, task_type, specification_id, commit=True): + if isinstance(mom_id, int) and mom_id < 0: + mom_id = None - return task_status, task_type + if isinstance(otdb_id, int) and otdb_id < 0: + otdb_id = None - def insertTask(self, mom_id, otdb_id, task_status, task_type, specification_id, commit=True): logger.info('insertTask mom_id=%s, otdb_id=%s, task_status=%s, task_type=%s, specification_id=%s' % (mom_id, otdb_id, task_status, task_type, specification_id)) task_status, task_type = self._convertTaskTypeAndStatusToIds(task_status, task_type) @@ -247,9 +387,9 @@ class RADatabase: def updateTaskStatusForOtdbId(self, otdb_id, task_status, commit=True): '''converts task_status and task_type to id's in case one and/or the other are strings''' - if task_status and isinstance(task_status, basestring): + if task_status is not None and isinstance(task_status, basestring): #convert task_status string to task_status.id - task_status = self.getTaskStatusId(task_status) + task_status = self.getTaskStatusId(task_status, True) query = '''UPDATE resource_allocation.task SET (status_id) = (%s) @@ -301,9 +441,14 @@ class RADatabase: return self.cursor.rowcount > 0 - def getTaskPredecessorIds(self): - query = '''SELECT * from resource_allocation.task_predecessor tp;''' - items = list(self._executeQuery(query, fetch=_FETCH_ALL)) + def getTaskPredecessorIds(self, id=None): + query = '''SELECT * from resource_allocation.task_predecessor tp''' + + if id is not None : + query += ' WHERE id=%s' + + items = list(self._executeQuery(query, [id] if id is not None else None, fetch=_FETCH_ALL)) + predIdDict = {} for item in items: taskId = item['task_id'] @@ -312,9 +457,14 @@ class RADatabase: predIdDict[taskId].append(item['predecessor_id']) return predIdDict - def getTaskSuccessorIds(self): + def getTaskSuccessorIds(self, id=None): query = '''SELECT * from resource_allocation.task_predecessor tp;''' - items = list(self._executeQuery(query, fetch=_FETCH_ALL)) + + if id is not None : + query += ' WHERE id=%s' + + items = list(self._executeQuery(query, [id] if id is not None else None, fetch=_FETCH_ALL)) + succIdDict = {} for item in items: predId = item['predecessor_id'] @@ -330,11 +480,11 @@ class RADatabase: items = list(self._executeQuery(query, [task_id], fetch=_FETCH_ALL)) return [x['predecessor_id'] for x in items] - def getTaskSuccessorIdsForTask(self, task_): + def getTaskSuccessorIdsForTask(self, task_id): query = '''SELECT * from resource_allocation.task_predecessor tp WHERE tp.predecessor_id = %s;''' - items = list(self._executeQuery(query, [task_], fetch=_FETCH_ALL)) + items = list(self._executeQuery(query, [task_id], fetch=_FETCH_ALL)) return [x['task_id'] for x in items] def insertTaskPredecessor(self, task_id, predecessor_id, commit=True): @@ -343,21 +493,43 @@ class RADatabase: VALUES (%s, %s) RETURNING id;''' - id = self._executeQuery(query, (task_id, predecessor_id), fetch=_FETCH_ONE)['id'] + result = self._executeQuery(query, (task_id, predecessor_id), fetch=_FETCH_ONE) + if commit: self.commit() - return id + + if result and 'id' in result: + return result['id'] + + return None def insertTaskPredecessors(self, task_id, predecessor_ids, commit=True): ids = [self.insertTaskPredecessor(task_id, predecessor_id, False) for predecessor_id in predecessor_ids] + ids = [x for x in ids if x is not None] + if commit: self.commit() return ids - def getSpecifications(self): - query = '''SELECT * from resource_allocation.specification;''' + def getSpecifications(self, specification_ids = None): + query = '''SELECT * from resource_allocation.specification''' - return list(self._executeQuery(query, fetch=_FETCH_ALL)) + conditions = [] + qargs = [] + + if specification_ids is not None: + if isinstance(specification_ids, int): # just a single id + conditions.append('id = %s') + qargs.append(specification_ids) + else: #assume a list/enumerable of id's + if len(specification_ids): + conditions.append('id in %s') + qargs.append(tuple(specification_ids)) + + if conditions: + query += ' WHERE ' + ' AND '.join(conditions) + + return list(self._executeQuery(query, qargs, fetch=_FETCH_ALL)) def getSpecification(self, specification_id): query = '''SELECT * from resource_allocation.specification spec @@ -365,14 +537,14 @@ class RADatabase: return list(self._executeQuery(query, [specification_id], fetch=_FETCH_ALL)) - def insertSpecification(self, starttime, endtime, content, commit=True): - logger.info('insertSpecification starttime=%s, endtime=%s' % (starttime, endtime)) + def insertSpecification(self, starttime, endtime, content, cluster, commit=True): + logger.info('insertSpecification starttime=%s, endtime=%s cluster=%s' % (starttime, endtime, cluster)) query = '''INSERT INTO resource_allocation.specification - (starttime, endtime, content) - VALUES (%s, %s, %s) + (starttime, endtime, content, cluster) + VALUES (%s, %s, %s, %s) RETURNING id;''' - id = self._executeQuery(query, (starttime, endtime, content), fetch=_FETCH_ONE)['id'] + id = self._executeQuery(query, (starttime, endtime, content, cluster), fetch=_FETCH_ONE)['id'] if commit: self.commit() return id @@ -387,7 +559,7 @@ class RADatabase: self.commit() return self.cursor.rowcount > 0 - def updateSpecification(self, specification_id, starttime=None, endtime=None, content=None, commit=True): + def updateSpecification(self, specification_id, starttime=None, endtime=None, content=None, cluster=None, commit=True): fields = [] values = [] @@ -403,6 +575,10 @@ class RADatabase: fields.append('content') values.append(content) + if cluster is not None : + fields.append('cluster') + values.append(cluster) + values.append(specification_id) query = '''UPDATE resource_allocation.specification @@ -487,7 +663,7 @@ class RADatabase: if isinstance(resource_ids, int): # just a single id conditions.append('id = %s') qargs.append(resource_ids) - elif len(resource_ids) > 0: # a list of id's + else: #assume a list/enumerable of id's conditions.append('id in %s') qargs.append(tuple(resource_ids)) @@ -640,8 +816,26 @@ class RADatabase: raise KeyError('No such resource_claim_property_type: %s Valid values are: %s' % (type_name, ', '.join(self.getResourceClaimPropertyTypeNames()))) + def getResourceClaimPropertyIOTypes(self): + query = '''SELECT * from resource_allocation.resource_claim_property_io_type;''' + + return list(self._executeQuery(query, fetch=_FETCH_ALL)) + + def getResourceClaimPropertyIOTypeNames(self): + return [x['name'] for x in self.getResourceClaimPropertyIOTypes()] + + def getResourceClaimPropertyIOTypeId(self, io_type_name): + query = '''SELECT id from resource_allocation.resource_claim_property_io_type + WHERE name = %s;''' + result = self._executeQuery(query, [io_type_name], fetch=_FETCH_ONE) + + if result: + return result['id'] + + raise KeyError('No such resource_claim_property_io_type: %s Valid values are: %s' % (io_type_name, ', '.join(self.getResourceClaimPropertyIOTypeNames()))) + def getResourceClaimProperties(self, claim_ids=None, task_id=None): - query = '''SELECT rcpv.id, rcpv.resource_claim_id, rcpv.value, rcpv.type_id, rcpv.type_name, sap.number as sap_nr + query = '''SELECT rcpv.id, rcpv.resource_claim_id, rcpv.value, rcpv.type_id, rcpv.type_name, rcpv.io_type_id, rcpv.io_type_name, sap.number as sap_nr FROM resource_allocation.resource_claim_property_view rcpv LEFT JOIN resource_allocation.sap sap on rcpv.sap_id = sap.id''' @@ -652,7 +846,7 @@ class RADatabase: if isinstance(claim_ids, int): # just a single id conditions.append('rcpv.resource_claim_id = %s') qargs.append(claim_ids) - elif len(claim_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('rcpv.resource_claim_id in %s') qargs.append(tuple(claim_ids)) @@ -671,15 +865,19 @@ class RADatabase: return properties - def insertResourceClaimProperty(self, claim_id, property_type, value, commit=True): - return self.insertResourceClaimProperties([(claim_id, property_type, value)], commit) + def insertResourceClaimProperty(self, claim_id, property_type, value, io_type, commit=True): + return self.insertResourceClaimProperties([(claim_id, property_type, value, io_type)], commit) def insertResourceClaimProperties(self, props, commit=True): if not props: return [] + # props is a list of tuples + # each tuple prop is encoded as: (claim_id, type, value, io_type, sap_nr) + # index: (0 , 1 , 2 , 3 , 4 ) + # first insert unique sap numbers - claim_sap_nrs = list(set([(p[0], p[3]) for p in props if p[3] is not None])) + claim_sap_nrs = list(set([(p[0], p[4]) for p in props if p[4] is not None])) sap_ids = self.insertSAPNumbers(claim_sap_nrs, False) if sap_ids == None: @@ -700,19 +898,25 @@ class RADatabase: type_strings = set([p[1] for p in props if isinstance(p[1], basestring)]) type_string2id = {t:self.getResourceClaimPropertyTypeId(t) for t in type_strings} + # convert all property io_type strings to id's + io_type_strings = set([p[3] for p in props if isinstance(p[3], basestring)]) + io_type_string2id = {t:self.getResourceClaimPropertyIOTypeId(t) for t in io_type_strings} + # finally we have all the info we need, # so we can build the bulk property insert query - insert_values = ','.join(self.cursor.mogrify('(%s, %s, %s, %s)', + insert_values = ','.join(self.cursor.mogrify('(%s, %s, %s, %s, %s)', (p[0], type_string2id[p[1]] if isinstance(p[1], basestring) else p[1], p[2], - claim_id2sap_nr2sap_id[p[0]].get(p[3]) if + io_type_string2id[p[3]] if + isinstance(p[3], basestring) else p[3], + claim_id2sap_nr2sap_id[p[0]].get(p[4]) if p[0] in claim_id2sap_nr2sap_id else None)) for p in props) query = '''INSERT INTO resource_allocation.resource_claim_property - (resource_claim_id, type_id, value, sap_id) + (resource_claim_id, type_id, value, io_type_id, sap_id) VALUES {values} RETURNING id;'''.format(values=insert_values) @@ -777,7 +981,7 @@ class RADatabase: if isinstance(claim_ids, int): # just a single id conditions.append('id = %s') qargs.append(claim_ids) - elif len(claim_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('id in %s') qargs.append(tuple(claim_ids)) @@ -793,7 +997,7 @@ class RADatabase: if isinstance(resource_ids, int): # just a single id conditions.append('resource_id = %s') qargs.append(resource_ids) - elif len(resource_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('resource_id in %s') qargs.append(tuple(resource_ids)) @@ -904,7 +1108,7 @@ class RADatabase: except Exception as e: logger.error("Invalid claim dict, rolling back. %s" % e) self.rollback() - return None + return [] try: # use psycopg2 mogrify to parse and build the insert values @@ -914,7 +1118,7 @@ class RADatabase: except Exception as e: logger.error("Invalid input, rolling back: %s\n%s" % (claim_values, e)) self.rollback() - return None + return [] query = '''INSERT INTO resource_allocation.resource_claim (resource_id, task_id, starttime, endtime, status_id, session_id, claim_size, username, user_id) @@ -926,20 +1130,20 @@ class RADatabase: if not claimIds or [x for x in claimIds if x < 0]: logger.error("One or more claims cloud not be inserted. Rolling back.") self.rollback() - return None + return [] # gather all properties for all claims - # store them as list of (claim_id, prop_type, prop_value, sap_nr) tuples + # store them as list of (claim_id, prop_type, prop_value, io_type, sap_nr) tuples properties = [] for claim_id, claim in zip(claimIds, claims): if 'properties' in claim and len(claim['properties']) > 0: - claim_props = [(claim_id, p['type'], p['value'], p.get('sap_nr')) for p in claim['properties']] + claim_props = [(claim_id, p['type'], p['value'], p.get('io_type', 0), p.get('sap_nr')) for p in claim['properties']] properties += claim_props if properties: property_ids = self.insertResourceClaimProperties(properties, False) if property_ids == None: - return None + return [] # get the claims as they were inserted # and validate them against all other claims @@ -966,7 +1170,7 @@ class RADatabase: def updateResourceClaims(self, resource_claim_ids, resource_id=None, task_id=None, starttime=None, endtime=None, status=None, session_id=None, claim_size=None, username=None, user_id=None, validate=True, commit=True): if not resource_claim_ids: - return + return True logger.info("updateResourceClaims for %d claims" % len(resource_claim_ids)) @@ -1050,6 +1254,8 @@ class RADatabase: if starttime or endtime: task = self.getTask(task_id) + if task == None: + return False updated &= self.updateSpecification(task['specification_id'], starttime=starttime, endtime=endtime, commit=False) if (starttime or endtime or claim_status is not None or session_id is not None or @@ -1057,14 +1263,15 @@ class RADatabase: # update the claims as well # updateResourceClaims also validates the updated claims claim_ids = [c['id'] for c in claimsBeforeUpdate] - updated &= self.updateResourceClaims(claim_ids, - starttime=starttime, - endtime=endtime, - status=claim_status, - session_id=session_id, - username=username, user_id=user_id, - validate=True, - commit=False) + if claim_ids: + updated &= self.updateResourceClaims(claim_ids, + starttime=starttime, + endtime=endtime, + status=claim_status, + session_id=session_id, + username=username, user_id=user_id, + validate=True, + commit=False) # because we moved or changed the status of these claims, # validate the claims 'underneath' which may have been in conflict @@ -1105,9 +1312,10 @@ class RADatabase: claimDict = {c['id']:c for c in claims} resource_ids = list(set([c['resource_id'] for c in claims])) - task_ids = list(set(c['task_id'] for c in claims)) min_starttime = min(c['starttime'] for c in claims) max_endtime = min(c['endtime'] for c in claims) + task_ids = list(set(c['task_id'] for c in claims)) + taskDict = {t['id']:t for t in self.getTasks(task_ids=task_ids)} logger.info("validating status of %d resource claim(s) for task_id(s) %s" % (len(claims), to_csv_string(task_ids))) @@ -1136,24 +1344,31 @@ class RADatabase: if claim['id'] not in claimDict: resource2otherClaims[claim['resource_id']].append(claim) - for claim_id, claim in claimDict.items(): - claimSize = claim['claim_size'] - resource_id = claim['resource_id'] - resource = resources[resource_id] - resourceOtherClaims = resource2otherClaims[resource_id] - totalOtherClaimSize = sum(c['claim_size'] for c in resourceOtherClaims) - - logger.info('resource_id=%s claimSize=%s totalOtherClaimSize=%s total=%s available_capacity=%s' % - (resource_id, - claimSize, - totalOtherClaimSize, - totalOtherClaimSize + claimSize, - resource['available_capacity'])) - - if totalOtherClaimSize + claimSize >= resource['available_capacity']: - newClaimStatuses[conflistStatusId].append(claim_id) - elif claim['status_id'] != allocatedStatusId: - newClaimStatuses[claimedStatusId].append(claim_id) + # TODO: claim conflict computions below are incorrect + # they cause observations/pipelines to go to conflict, although there is ample space + #for claim_id, claim in claimDict.items(): + #task_id = claim['task_id'] + #task_status = taskDict[task_id]['status'] + #if task_status in ['prepared', 'approved', 'on_hold', 'conflict', 'prescheduled']: + #claimSize = claim['claim_size'] + #resource_id = claim['resource_id'] + #resource = resources[resource_id] + #resourceOtherClaims = resource2otherClaims[resource_id] + #totalOtherClaimSize = sum(c['claim_size'] for c in resourceOtherClaims) + + #logger.info('resource_id=%s claimSize=%s totalOtherClaimSize=%s total=%s available_capacity=%s' % + #(resource_id, + #claimSize, + #totalOtherClaimSize, + #totalOtherClaimSize + claimSize, + #resource['available_capacity'])) + + #if totalOtherClaimSize + claimSize >= resource['available_capacity']: + #logger.info("totalOtherClaimSize (%s) + claimSize (%s) >= resource_available_capacity %s for claim %s on resource %s %s for task %s", + #totalOtherClaimSize, claimSize, resource['available_capacity'], claim_id, resource_id, resource['name'], task_id) + #newClaimStatuses[conflistStatusId].append(claim_id) + #elif claim['status_id'] != allocatedStatusId: + #newClaimStatuses[claimedStatusId].append(claim_id) if newClaimStatuses: for status_id, claim_ids in newClaimStatuses.items(): @@ -1173,26 +1388,40 @@ class RADatabase: if commit: self.commit() - def insertSpecificationAndTask(self, mom_id, otdb_id, task_status, task_type, starttime, endtime, content, commit=True): + def insertSpecificationAndTask(self, mom_id, otdb_id, task_status, task_type, starttime, endtime, content, cluster, commit=True): ''' Insert a new specification and task in one transaction. Removes existing task with same otdb_id if present in the same transaction. + Removes existing task with same mom_id if present in the same transaction. ''' try: - task = self.getTask(otdb_id=otdb_id) - - if task: + existing_task = self.getTask(otdb_id=otdb_id) + if existing_task: # delete old specification, task, and resource claims using cascaded delete - self.deleteSpecification(task['specification_id'], False) + self.deleteSpecification(existing_task['specification_id'], False) + else: + existing_task = self.getTask(mom_id=mom_id) + if existing_task: + # delete old specification, task, and resource claims using cascaded delete + self.deleteSpecification(existing_task['specification_id'], False) - specId = self.insertSpecification(starttime, endtime, content, False) + specId = self.insertSpecification(starttime, endtime, content, cluster, False) taskId = self.insertTask(mom_id, otdb_id, task_status, task_type, specId, False) if specId >= 0 and taskId >= 0: + # restore "old" predecessor/successor relationships if needed + if existing_task: + if existing_task['predecessor_ids']: + self.insertTaskPredecessors(taskId, existing_task['predecessor_ids'], False) + if existing_task['successor_ids']: + for suc_id in existing_task['successor_ids']: + self.insertTaskPredecessor(suc_id, taskId, False) + if commit: self.commit() return {'inserted': True, 'specification_id': specId, 'task_id': taskId} - except: + except Exception as e: + logger.error(e) self.rollback() return {'inserted': False, 'specification_id': None, 'task_id': None} @@ -1207,7 +1436,7 @@ class RADatabase: if isinstance(task_ids, int): # just a single id conditions.append('task_id = %s') qargs.append(task_ids) - elif len(task_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('task_id in %s') qargs.append(tuple(task_ids)) @@ -1250,7 +1479,7 @@ class RADatabase: if isinstance(claim_ids, int): # just a single id conditions.append('id = %s') qargs.append(claim_ids) - elif len(claim_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('id in %s') qargs.append(tuple(claim_ids)) @@ -1258,7 +1487,7 @@ class RADatabase: if isinstance(resource_ids, int): # just a single id conditions.append('resource_id = %s') qargs.append(resource_ids) - elif len(resource_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('resource_id in %s') qargs.append(tuple(resource_ids)) @@ -1266,7 +1495,7 @@ class RADatabase: if isinstance(task_ids, int): # just a single id conditions.append('task_id = %s') qargs.append(task_ids) - elif len(task_ids) > 0: # list of id's + else: #assume a list/enumerable of id's conditions.append('task_id in %s') qargs.append(tuple(task_ids)) @@ -1393,242 +1622,29 @@ if __name__ == '__main__': logger.info("Using dbcreds: %s" % dbcreds.stringWithHiddenPassword()) db = RADatabase(dbcreds=dbcreds, log_queries=True) + exit() def resultPrint(method): print '\n-- ' + str(method.__name__) + ' --' print '\n'.join([str(x) for x in method()]) + resultPrint(db.getTaskStatuses) + resultPrint(db.getTaskStatusNames) + resultPrint(db.getTaskTypes) + resultPrint(db.getTaskTypeNames) + resultPrint(db.getResourceClaimStatuses) + resultPrint(db.getResourceClaimStatusNames) + resultPrint(db.getUnits) + resultPrint(db.getUnitNames) + resultPrint(db.getResourceTypes) + resultPrint(db.getResourceTypeNames) + resultPrint(db.getResourceGroupTypes) + resultPrint(db.getResourceGroupTypeNames) + resultPrint(db.getResources) + resultPrint(db.getResourceGroups) + resultPrint(db.getResourceGroupMemberships) resultPrint(db.getTasks) + resultPrint(db.getSpecifications) + resultPrint(db.getResourceClaims) + resultPrint(db.getResourceClaimProperties) - for t in db.getTasks(): - print db.getTask(t['id']) - - exit() - - #print db.getResourceClaims(task_id=440) - #print - #print db.getResourceClaims(lower_bound=datetime.utcnow() + timedelta(days=9)) - #print - #print db.getResourceClaims(upper_bound=datetime.utcnow() + timedelta(days=19)) - #print - #print db.getResourceClaims(status='allocated') - #print - #print db.getResourceClaims(status='claimed') - #print - #print db.getResourceClaims(resource_type='storage') - - #resultPrint(db.getTaskStatuses) - #resultPrint(db.getTaskStatusNames) - #resultPrint(db.getTaskTypes) - #resultPrint(db.getTaskTypeNames) - #resultPrint(db.getResourceClaimStatuses) - #resultPrint(db.getResourceClaimStatusNames) - #resultPrint(db.getUnits) - #resultPrint(db.getUnitNames) - #resultPrint(db.getResourceTypes) - #resultPrint(db.getResourceTypeNames) - #resultPrint(db.getResourceGroupTypes) - #resultPrint(db.getResourceGroupTypeNames) - #resultPrint(db.getResources) - #resultPrint(db.getResourceGroups) - #resultPrint(db.getResourceGroupMemberships) - #resultPrint(db.getTasks) - #print db.getTaskPredecessorIds() - #print db.getTaskSuccessorIds() - #resultPrint(db.getSpecifications) - #resultPrint(db.getResourceClaims) - - #claims = db.getResourceClaims() - #db.updateTaskAndResourceClaims(claims[0]['task_id'], starttime=claims[1]['starttime'], endtime=claims[1]['endtime']) - #print - #print db.getResourceClaims() - - #resultPrint(db.getResourceClaims) - - - db.updateResourceAvailability(0, available_capacity=2) - exit(0) - - import pprint - pprint.pprint(db.getTaskConflictReasons()) - db.updateTask(21, task_status='conflict') - db.insertTaskConflicts(21, [1, 2, 3]) - pprint.pprint(db.getTaskConflictReasons()) - db.updateTask(21, task_status='scheduled') - pprint.pprint(db.getTaskConflictReasons()) - db.insertTaskConflicts(21, [1, 2, 3]) - pprint.pprint(db.getTaskConflictReasons()) - - pprint.pprint(db.getResourceClaimConflictReasons(task_ids=22)) - #pprint.pprint(db.getResourceUsages()) - - exit(0) - - for s in db.getSpecifications(): - db.deleteSpecification(s['id']) - - resources = db.getResources() - - #task_id = db.insertSpecificationAndTask(1234, 5678, 600, 0, datetime.utcnow(), datetime.utcnow() + timedelta(hours=1), "", False)['task_id'] - #task = db.getTask(task_id) - - #claim = {'resource_id':resources[0]['id'], - #'starttime':task['starttime'], - #'endtime':task['endtime'], - #'status':'claimed', - #'claim_size':1} - #db.insertResourceClaims(task_id, [claim], 1, 'anonymous', -1, False) - - #claim = {'resource_id':resources[1]['id'], - #'starttime':task['starttime'], - #'endtime':task['endtime'], - #'status':'claimed', - #'claim_size':1, - #'properties':[{'type':'nr_of_is_files', 'value':10},{'type':'nr_of_cs_files', 'value':20}]} - #db.insertResourceClaims(task_id, [claim], 1, 'anonymous', -1, False) - - #claim = {'resource_id':resources[2]['id'], - #'starttime':task['starttime'], - #'endtime':task['endtime'], - #'status':'claimed', - #'claim_size':1, - #'properties':[{'type':'nr_of_is_files', 'value':10, 'sap_nr':0 }, - #{'type':'nr_of_cs_files', 'value':20, 'sap_nr':0}, - #{'type':'nr_of_uv_files', 'value':30, 'sap_nr':1},]} - #db.insertResourceClaims(task_id, [claim], 1, 'anonymous', -1, False) - - #claim = {'resource_id':resources[3]['id'], - #'starttime':task['starttime'], - #'endtime':task['endtime'], - #'status':'claimed', - #'claim_size':1, - #'properties':[{'type':'nr_of_is_files', 'value':15 }, - #{'type':'nr_of_cs_files', 'value':25 }, - #{'type':'nr_of_is_files', 'value':10, 'sap_nr':0 }, - #{'type':'nr_of_cs_files', 'value':20, 'sap_nr':0}, - #{'type':'nr_of_uv_files', 'value':30, 'sap_nr':1},]} - #db.insertResourceClaims(task_id, [claim], 1, 'anonymous', -1, False) - - #db.commit() - #import pprint - #pprint.pprint(db.getResourceClaims(include_properties=True)) - #print '\n'.join(str(x) for x in db.getResourceClaims(include_properties=True)) - - - #c = db.cursor - #query = '''INSERT INTO resource_allocation.resource_claim - #(resource_id, task_id, starttime, endtime, status_id, session_id, claim_size, username, user_id) - #VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) - #RETURNING id;''' - - #print c.mogrify(query, [(0, 0, datetime.utcnow(), datetime.utcnow(), 200, 1, 1, 'piet', 1)]) - #exit(0) - - - #for s in db.getSpecifications(): - #db.deleteSpecification(s['id']) - - from lofar.common.datetimeutils import totalSeconds - begin = datetime.utcnow() - for i in range(5): - stepbegin = datetime.utcnow() - result = db.insertSpecificationAndTask(1234+i, 5678+i, 350, 0, datetime.utcnow() + timedelta(hours=1.25*i), datetime.utcnow() + timedelta(hours=1.25*i+1), "", False) - - #resultPrint(db.getSpecifications) - #resultPrint(db.getTasks) - - task = db.getTask(result['task_id']) - - claims = [{'resource_id':r['id'], - 'starttime':task['starttime'], - 'endtime':task['endtime'], - 'status': ['claimed', 'allocated', 'conflict'][i%3], - 'claim_size':1} for r in resources[:1]] - - for c in claims[:]: - c['properties'] = [{'type':0, 'value':10}, {'type':1, 'value':20}, {'type':2, 'value':30}] - - for i, c in enumerate(claims[:4]): - c['properties'][0]['sap_nr'] = i % 2 - - db.insertResourceClaims(task['id'], claims, 1, 'paulus', 1, False) - - #resultPrint(db.getResourceClaims) - #raw_input() - db.commit() - now = datetime.utcnow() - print totalSeconds(now - begin), totalSeconds(now - stepbegin) - - import pprint - pprint.pprint(db.getResourceUsages(resource_type='storage')) - - #resultPrint(db.getResourceClaims) - #resultPrint(db.getResourceClaimPropertyTypes) - ##resultPrint(db.getResourceClaimPropertyTypeNames) - ##resultPrint(db.getResourceClaimProperties) - - #print '\n'.join(str(x) for x in db.getResourceClaimProperties()) - #print '\n'.join(str(x) for x in db.getResourceClaimProperties(task_id=task['id'])) - #print '\n'.join(str(x) for x in db.getResourceClaims(include_properties=True)) - - #db.commit() - - #resultPrint(db.getTasks) - #resultPrint(db.getResourceClaims) - - #tasks = db.getTasks() - #db.updateTaskAndResourceClaims(tasks[0]['id'], endtime=tasks[1]['endtime']) - - #resultPrint(db.getTasks) - #resultPrint(db.getResourceClaims) - #db.updateTaskAndResourceClaims(tasks[0]['id'], endtime=tasks[0]['starttime'] + timedelta(hours=1)) - - #resultPrint(db.getTasks) - #resultPrint(db.getResourceClaims) - - - #claims = db.getResourceClaims() - #for c in claims: - #db.deleteResourceClaim(c['id']) - ##resultPrint(db.getResourceClaims) - - #predTaskId = None - #for i in range(2): - #specId = db.insertSpecification(datetime.utcnow(), datetime.utcnow() + timedelta(hours=4), "") - #taskId = db.insertTask(1234+i, 5678+i, 600, 0, specId) - - #if predTaskId: - #db.insertTaskPredecessor(taskId, predTaskId) - #predTaskId = taskId - - #resources = db.getResources() - #for r in resources: - #rcId = db.insertResourceClaim(r['id'], taskId, datetime.utcnow() + timedelta(hours=2*i), datetime.utcnow() + timedelta(hours=2*(i+1)), 0, 1, 10, 'einstein', -1) - - - ##tasks = db.getTasks() - ##for t in tasks: - ##db.deleteTask(t['id']) - ####resultPrint(db.getTasks) - ####resultPrint(db.getResourceClaims) - - #import random - - ##for i in range(1): - ##taskId = db.insertTask(1234, 5678, 600, 0, 1) - ##for j in range(2*i): - ##rcId = db.insertResourceClaim(j, taskId, datetime.utcnow() + timedelta(hours=4*i), datetime.utcnow() + timedelta(hours=4*i+3.5), 0, 4, 10, 'einstein', -1) - - ##time.sleep(0.5) - - ##resultPrint(db.getTasks) - ##resultPrint(db.getResourceClaims) - - #ts = db.getTaskStatuses() - - #tasks = sorted(db.getTasks(), key=lambda x: x['id']) - #for t in tasks: - #db.updateTask(t['id'], task_status=ts[random.randint(0, len(ts)-1)]['id']) - #time.sleep(0.01) - - #db.commit() diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbbuslistener.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbbuslistener.py index fcf3ff0cf918f61af87224ede09814caea4d2eeb..7f108839fbad36a1ad9a859cfc941ca7b59227c0 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbbuslistener.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbbuslistener.py @@ -61,28 +61,27 @@ class RADBBusListener(AbstractBusListener): logger.info("on%s: %s" % (msg.subject.replace(self.subject_prefix, ''), str(msg.content).replace('\n', ' '))) if msg.subject == '%sTaskUpdated' % self.subject_prefix: - self.onTaskUpdated(msg.content.get('old'), msg.content.get('new')) + self.onTaskUpdated(msg.content) elif msg.subject == '%sTaskInserted' % self.subject_prefix: - self.onTaskInserted(msg.content.get('new')) + self.onTaskInserted(msg.content) elif msg.subject == '%sTaskDeleted' % self.subject_prefix: - self.onTaskDeleted(msg.content.get('old')) + self.onTaskDeleted(msg.content) elif msg.subject == '%sResourceClaimUpdated' % self.subject_prefix: - self.onResourceClaimUpdated(msg.content.get('old'), msg.content.get('new')) + self.onResourceClaimUpdated(msg.content) elif msg.subject == '%sResourceClaimInserted' % self.subject_prefix: - self.onResourceClaimInserted(msg.content.get('new')) + self.onResourceClaimInserted(msg.content) elif msg.subject == '%sResourceClaimDeleted' % self.subject_prefix: - self.onResourceClaimDeleted(msg.content.get('old')) + self.onResourceClaimDeleted(msg.content) elif msg.subject == '%sResourceAvailabilityUpdated' % self.subject_prefix: - self.onResourceAvailabilityUpdated(msg.content.get('old'), msg.content.get('new')) + self.onResourceAvailabilityUpdated(msg.content) elif msg.subject == '%sResourceCapacityUpdated' % self.subject_prefix: - self.onResourceCapacityUpdated(msg.content.get('old'), msg.content.get('new')) + self.onResourceCapacityUpdated(msg.content) else: logger.error("RADBBusListener.handleMessage: unknown subject: %s" %str(msg.subject)) - def onTaskUpdated(self, old_task, new_task): + def onTaskUpdated(self, updated_task): '''onTaskUpdated is called upon receiving a TaskUpdated message. - :param old_task: dictionary with the task before the update - :param new_task: dictionary with the updated task''' + :param updated_task: dictionary with the updated task''' pass def onTaskInserted(self, new_task): @@ -90,15 +89,14 @@ class RADBBusListener(AbstractBusListener): :param new_task: dictionary with the inserted task''' pass - def onTaskDeleted(self, old_task): + def onTaskDeleted(self, old_task_id): '''onTaskDeleted is called upon receiving a TaskDeleted message. - :param old_task: dictionary with the deleted task''' + :param old_task_id: id of the deleted task''' pass - def onResourceClaimUpdated(self, old_claim, new_claim): + def onResourceClaimUpdated(self, updated_claim): '''onResourceClaimUpdated is called upon receiving a ResourceClaimUpdated message. - :param old_claim: dictionary with the claim before the update - :param new_claim: dictionary with the updated claim''' + :param updated_claim: dictionary with the updated claim''' pass def onResourceClaimInserted(self, new_claim): @@ -106,21 +104,19 @@ class RADBBusListener(AbstractBusListener): :param new_claim: dictionary with the inserted claim''' pass - def onResourceClaimDeleted(self, old_claim): + def onResourceClaimDeleted(self, old_claim_id): '''onResourceClaimDeleted is called upon receiving a ResourceClaimDeleted message. - :param old_claim: dictionary with the deleted claim''' + :param old_claim_id: id of the deleted claim''' pass - def onResourceAvailabilityUpdated(self, old_availability, new_availability): + def onResourceAvailabilityUpdated(self, updated_availability): '''onResourceAvailabilityUpdated is called upon receiving a ResourceAvailabilityUpdated message. - :param old_availability: dictionary with the resource availability before the update - :param new_availability: dictionary with the updated availability''' + :param updated_availability: dictionary with the updated availability''' pass - def onResourceCapacityUpdated(self, old_capacity, new_capacity): + def onResourceCapacityUpdated(self, updated_capacity): '''onResourceCapacityUpdated is called upon receiving a ResourceCapacityUpdated message. - :param old_capacity: dictionary with the resource capacity before the update - :param new_capacity: dictionary with the updated capacity''' + :param updated_capacity: dictionary with the updated capacity''' pass diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.ini b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.ini index 24abe5422b116fd9794bcf2714017944651994d2..b2a3326e2594f12361d75523a329a3516010dec3 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.ini +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.ini @@ -1,5 +1,5 @@ [program:radbpglistener] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;radbpglistener' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec radbpglistener' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.py index 9ab46514d73afc5f13fed049e05109a999114b29..5fedde628a3edafcdae6576004f497b620f212e9 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.py @@ -35,71 +35,109 @@ from lofar.messaging import EventMessage, ToBus from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_BUSNAME, DEFAULT_NOTIFICATION_PREFIX from lofar.common import dbcredentials +from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_BUSNAME as DEFAULT_RADB_BUSNAME +from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RADB_SERVICENAME + logger = logging.getLogger(__name__) class RADBPGListener(PostgresListener): def __init__(self, - busname=DEFAULT_NOTIFICATION_BUSNAME, + notification_busname=DEFAULT_NOTIFICATION_BUSNAME, notification_prefix=DEFAULT_NOTIFICATION_PREFIX, + radb_busname=DEFAULT_RADB_BUSNAME, + radb_servicename=DEFAULT_RADB_SERVICENAME, dbcreds=None, broker=None): super(RADBPGListener, self).__init__(dbcreds.host, dbcreds.database, dbcreds.user, dbcreds.password) self.notification_prefix = notification_prefix - self.event_bus = ToBus(busname, broker=broker) + self.event_bus = ToBus(notification_busname, broker=broker) + + self.rarpc = RARPC(busname=radb_busname, servicename=radb_servicename, broker=broker) - self.subscribe('task_update_with_task_view', self.onTaskUpdated) - self.subscribe('task_insert_with_task_view', self.onTaskInserted) + self.subscribe('task_update', self.onTaskUpdated) + self.subscribe('task_insert', self.onTaskInserted) self.subscribe('task_delete', self.onTaskDeleted) + self.subscribe('task_predecessor_insert_column_task_id', self.onTaskPredecessorChanged) + self.subscribe('task_predecessor_update_column_task_id', self.onTaskPredecessorChanged) + self.subscribe('task_predecessor_delete_column_task_id', self.onTaskPredecessorChanged) + + self.subscribe('task_predecessor_insert_column_predecessor_id', self.onTaskSuccessorChanged) + self.subscribe('task_predecessor_update_column_predecessor_id', self.onTaskSuccessorChanged) + self.subscribe('task_predecessor_delete_column_predecessor_id', self.onTaskSuccessorChanged) + # when the specification starttime and endtime are updated, then that effects the task as well - # so subscribe to specification_update, and use task_view as view_for_row - self.subscribe('specification_update_with_task_view', self.onSpecificationUpdated) + self.subscribe('specification_update', self.onSpecificationUpdated) - self.subscribe('resource_claim_update_with_resource_claim_view', self.onResourceClaimUpdated) - self.subscribe('resource_claim_insert_with_resource_claim_view', self.onResourceClaimInserted) + self.subscribe('resource_claim_update', self.onResourceClaimUpdated) + self.subscribe('resource_claim_insert', self.onResourceClaimInserted) self.subscribe('resource_claim_delete', self.onResourceClaimDeleted) self.subscribe('resource_availability_update', self.onResourceAvailabilityUpdated) self.subscribe('resource_capacity_update', self.onResourceCapacityUpdated) def onTaskUpdated(self, payload = None): - self._sendNotification('TaskUpdated', payload, ['starttime', 'endtime']) + # Send notification for the given updated task + task_id = payload + task = self.rarpc.getTask(task_id) + self._sendNotification('TaskUpdated', task) + + # The "blocked_by_ids" property of the given task's successors might have been updated due to the given task + # status being updated. Therefore also send a notification for these successors - lazily ignoring that they + # might not have changed. + suc_sched_tasks = self.rarpc.getTasks(task_ids=task['successor_ids'], task_status='scheduled') + for suc_sched_task in suc_sched_tasks: + self._sendNotification('TaskUpdated', suc_sched_task) def onTaskInserted(self, payload = None): - self._sendNotification('TaskInserted', payload, ['starttime', 'endtime']) + self._sendNotification('TaskInserted', self.rarpc.getTask(payload)) def onTaskDeleted(self, payload = None): self._sendNotification('TaskDeleted', payload) + def onTaskPredecessorChanged(self, task_id): + logger.info('onTaskPredecessorChanged(task_id=%s)', task_id) + self._sendNotification('TaskUpdated', self.rarpc.getTask(task_id)) + + def onTaskSuccessorChanged(self, task_id): + logger.info('onTaskSuccessorChanged(task_id=%s)', task_id) + self._sendNotification('TaskUpdated', self.rarpc.getTask(task_id)) + def onSpecificationUpdated(self, payload = None): # when the specification starttime and endtime are updated, then that effects the task as well - # so send a TaskUpdated notification - self._sendNotification('TaskUpdated', payload, ['starttime', 'endtime']) + self._sendNotification('TaskUpdated', self.rarpc.getTask(specification_id=payload)) def onResourceClaimUpdated(self, payload = None): - self._sendNotification('ResourceClaimUpdated', payload, ['starttime', 'endtime']) + self._sendNotification('ResourceClaimUpdated', self.rarpc.getResourceClaim(payload)) def onResourceClaimInserted(self, payload = None): - self._sendNotification('ResourceClaimInserted', payload, ['starttime', 'endtime']) + self._sendNotification('ResourceClaimInserted', self.rarpc.getResourceClaim(payload)) def onResourceClaimDeleted(self, payload = None): self._sendNotification('ResourceClaimDeleted', payload) def onResourceAvailabilityUpdated(self, payload = None): - self._sendNotification('ResourceAvailabilityUpdated', payload) + r = self.rarpc.getResources(resource_ids=[payload], include_availability=True)[0] + r = {k:r[k] for k in ['id', 'active']} + self._sendNotification('ResourceAvailabilityUpdated', r) def onResourceCapacityUpdated(self, payload = None): - self._sendNotification('ResourceCapacityUpdated', payload) + r = self.rarpc.getResources(resource_ids=[payload], include_availability=True)[0] + r = {k:r[k] for k in ['id', 'total_capacity', 'available_capacity', 'used_capacity']} + self._sendNotification('ResourceCapacityUpdated', r) def __enter__(self): super(RADBPGListener, self).__enter__() self.event_bus.open() + self.rarpc.open() return self def __exit__(self, exc_type, exc_val, exc_tb): super(RADBPGListener, self).__exit__(exc_type, exc_val, exc_tb) self.event_bus.close() + self.rarpc.close() def _formatTimestampsAsIso(self, fields, contentDict): '''convert all requested fields in the contentDict to proper isoformat datetime strings. @@ -109,63 +147,47 @@ class RADBPGListener(PostgresListener): So, parse the requested fields, and return them as datetime. ''' try: - for state in ('old', 'new'): - if state in contentDict: - for field in fields: - try: - if contentDict[state] and field in contentDict[state]: - timestampStr = contentDict[state][field] - formatStr = '%Y-%m-%dT%H:%M:%S' if 'T' in timestampStr else '%Y-%m-%d %H:%M:%S' - if timestampStr.rfind('.') > -1: - formatStr += '.%f' - - timestamp = datetime.strptime(timestampStr, formatStr) - - contentDict[state][field] = timestamp - except Exception as e: - logger.error('Could not convert field \'%s\' to datetime: %s' % (field, e)) + for field in fields: + try: + if field in contentDict: + timestampStr = contentDict[field] + formatStr = '%Y-%m-%dT%H:%M:%S' if 'T' in timestampStr else '%Y-%m-%d %H:%M:%S' + if timestampStr.rfind('.') > -1: + formatStr += '.%f' - return contentDict - except Exception as e: - logger.error('Could not parse payload: %s\n%s' % (contentDict, e)) + timestamp = datetime.strptime(timestampStr, formatStr) + contentDict[field] = timestamp + except Exception as e: + logger.error('Could not convert field \'%s\' to datetime: %s' % (field, e)) - def _sendNotification(self, subject, payload, timestampFields = None): - try: - content = json.loads(payload) - - if 'new' in content and content['new'] and 'old' in content and content['old']: - # check if new and old are equal. - # however, new and old can be based on different views, - # so, only check the values for the keys they have in common - new_keys = set(content['new'].keys()) - old_keys = set(content['old'].keys()) - common_keys = new_keys & old_keys - equal_valued_keys = [k for k in common_keys if content['new'][k] == content['old'][k]] - if len(equal_valued_keys) == len(common_keys): - logger.info('new and old values are equal, not sending notification. %s' % (content['new'])) - return - - if timestampFields: - content = self._formatTimestampsAsIso(timestampFields, content) + return contentDict except Exception as e: - logger.error('Could not parse payload: %s\n%s' % (payload, e)) - content=None + logger.error('Error while convering timestamp fields \'%s\'in %s\n%s' % (fields, contentDict, e)) + def _sendNotification(self, subject, contentDict): try: - msg = EventMessage(context=self.notification_prefix + subject, content=content) - logger.info('Sending notification %s: %s' % (subject, str(content).replace('\n', ' '))) - self.event_bus.send(msg) + if subject and contentDict: + msg = EventMessage(context=self.notification_prefix + subject, content=contentDict) + logger.info('Sending notification %s: %s' % (subject, str(contentDict).replace('\n', ' '))) + self.event_bus.send(msg) except Exception as e: logger.error(str(e)) def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + # Check the invocation arguments parser = OptionParser("%prog [options]", description='runs the radb postgres listener which listens to changes on some tables in the radb and publishes the changes as notifications on the bus.') parser.add_option('-q', '--broker', dest='broker', type='string', default=None, help='Address of the qpid broker, default: localhost') - parser.add_option("-b", "--busname", dest="busname", type="string", default=DEFAULT_NOTIFICATION_BUSNAME, help="Name of the publication bus on the qpid broker, [default: %default]") - parser.add_option("-n", "--notification_prefix", dest="notification_prefix", type="string", default=DEFAULT_NOTIFICATION_PREFIX, help="The prefix for all notifications of this publisher, [default: %default]") + parser.add_option('--radb_busname', dest='radb_busname', type='string', default=DEFAULT_RADB_BUSNAME, help='Name of the bus exchange on the qpid broker on which the radbservice listens, default: %default') + parser.add_option('--radb_servicename', dest='radb_servicename', type='string', default=DEFAULT_RADB_SERVICENAME, help='Name of the radbservice, default: %default') + parser.add_option('--radb_notification_busname', dest='radb_notification_busname', type='string', default=DEFAULT_NOTIFICATION_BUSNAME, help='Name of the notification bus exchange on the qpid broker on which the radb notifications are published, default: %default') + parser.add_option("--radb_notification_prefix", dest="radb_notification_prefix", type="string", default=DEFAULT_NOTIFICATION_PREFIX, help="The prefix for all notifications of this publisher, [default: %default]") + parser.add_option_group(dbcredentials.options_group(parser)) parser.set_defaults(dbcredentials="RADB") (options, args) = parser.parse_args() @@ -177,8 +199,10 @@ def main(): logger.info("Using dbcreds: %s" % dbcreds.stringWithHiddenPassword()) - with RADBPGListener(busname=options.busname, - notification_prefix=options.notification_prefix, + with RADBPGListener(notification_busname=options.radb_notification_busname, + notification_prefix=options.radb_notification_prefix, + radb_busname=options.radb_busname, + radb_servicename=options.radb_servicename, dbcreds=dbcreds, broker=options.broker) as listener: listener.waitWhileListening() diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_notifications.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_notifications.sql index afc072544f1f1edb1f6f9221a1d49afdcbb5f9f6..05906465401a4c48d29c0edb8b0471d672015140 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_notifications.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_notifications.sql @@ -6,50 +6,48 @@ --this RADBPGListener then broadcasts the event on the lofar bus. -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_INSERT_with_task_view ON resource_allocation.task CASCADE; -DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_INSERT_with_task_view(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_INSERT ON resource_allocation.task CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_INSERT(); -CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_INSERT_with_task_view() +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_INSERT() RETURNS TRIGGER AS $$ -DECLARE -new_row_from_view resource_allocation.task_view%ROWTYPE; +DECLARE payload text; BEGIN -select * into new_row_from_view from resource_allocation.task_view where id = NEW.id LIMIT 1; -PERFORM pg_notify(CAST('task_insert_with_task_view' AS text), -'{"old":' || 'null' || ',"new":' || row_to_json(new_row_from_view)::text || '}'); +SELECT CAST(NEW.id AS text) INTO payload; +PERFORM pg_notify(CAST('task_insert' AS text), payload); RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_INSERT_with_task_view +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_INSERT AFTER INSERT ON resource_allocation.task FOR EACH ROW -EXECUTE PROCEDURE resource_allocation.NOTIFY_task_INSERT_with_task_view(); +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_INSERT(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_UPDATE_with_task_view ON resource_allocation.task CASCADE; -DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_UPDATE_with_task_view(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_UPDATE ON resource_allocation.task CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_UPDATE(); -CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_UPDATE_with_task_view() +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_UPDATE() RETURNS TRIGGER AS $$ -DECLARE -new_row_from_view resource_allocation.task_view%ROWTYPE; +DECLARE payload text; BEGIN -select * into new_row_from_view from resource_allocation.task_view where id = NEW.id LIMIT 1; -PERFORM pg_notify(CAST('task_update_with_task_view' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || row_to_json(new_row_from_view)::text || '}'); +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.id AS text) INTO payload; +PERFORM pg_notify(CAST('task_update' AS text), payload); +END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_UPDATE_with_task_view +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_UPDATE AFTER UPDATE ON resource_allocation.task FOR EACH ROW -EXECUTE PROCEDURE resource_allocation.NOTIFY_task_UPDATE_with_task_view(); +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_UPDATE(); DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_DELETE ON resource_allocation.task CASCADE; @@ -58,9 +56,10 @@ DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_DELETE(); CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_DELETE() RETURNS TRIGGER AS $$ +DECLARE payload text; BEGIN -PERFORM pg_notify(CAST('task_delete' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || 'null' || '}'); +SELECT CAST(OLD.id AS text) INTO payload; +PERFORM pg_notify(CAST('task_delete' AS text), payload); RETURN OLD; END; $$ LANGUAGE plpgsql; @@ -72,73 +71,201 @@ FOR EACH ROW EXECUTE PROCEDURE resource_allocation.NOTIFY_task_DELETE(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_specification_UPDATE_with_task_view ON resource_allocation.specification CASCADE; -DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_specification_UPDATE_with_task_view(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_INSERT_column_task_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_INSERT_column_task_id(); -CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_specification_UPDATE_with_task_view() +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_INSERT_column_task_id() RETURNS TRIGGER AS $$ -DECLARE -new_row_from_view resource_allocation.task_view%ROWTYPE; +DECLARE payload text; BEGIN -select * into new_row_from_view from resource_allocation.task_view where specification_id = NEW.id LIMIT 1; -PERFORM pg_notify(CAST('specification_update_with_task_view' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || row_to_json(new_row_from_view)::text || '}'); +SELECT CAST(NEW.task_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_insert_column_task_id' AS text), payload); RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_specification_UPDATE_with_task_view +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_INSERT_column_task_id +AFTER INSERT ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_INSERT_column_task_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_UPDATE_column_task_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_UPDATE_column_task_id(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_UPDATE_column_task_id() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.task_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_update_column_task_id' AS text), payload); +END IF; +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_UPDATE_column_task_id +AFTER UPDATE ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_UPDATE_column_task_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_DELETE_column_task_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_DELETE_column_task_id(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_DELETE_column_task_id() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +SELECT CAST(OLD.task_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_delete_column_task_id' AS text), payload); +RETURN OLD; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_DELETE_column_task_id +AFTER DELETE ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_DELETE_column_task_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_INSERT_column_predecessor_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_INSERT_column_predecessor_id(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_INSERT_column_predecessor_id() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +SELECT CAST(NEW.predecessor_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_insert_column_predecessor_id' AS text), payload); +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_INSERT_column_predecessor_id +AFTER INSERT ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_INSERT_column_predecessor_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_UPDATE_column_predecessor_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_UPDATE_column_predecessor_id(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_UPDATE_column_predecessor_id() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.predecessor_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_update_column_predecessor_id' AS text), payload); +END IF; +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_UPDATE_column_predecessor_id +AFTER UPDATE ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_UPDATE_column_predecessor_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_task_predecessor_DELETE_column_predecessor_id ON resource_allocation.task_predecessor CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_task_predecessor_DELETE_column_predecessor_id(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_task_predecessor_DELETE_column_predecessor_id() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +SELECT CAST(OLD.predecessor_id AS text) INTO payload; +PERFORM pg_notify(CAST('task_predecessor_delete_column_predecessor_id' AS text), payload); +RETURN OLD; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_task_predecessor_DELETE_column_predecessor_id +AFTER DELETE ON resource_allocation.task_predecessor +FOR EACH ROW +EXECUTE PROCEDURE resource_allocation.NOTIFY_task_predecessor_DELETE_column_predecessor_id(); + + +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_specification_UPDATE ON resource_allocation.specification CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_specification_UPDATE(); + + +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_specification_UPDATE() +RETURNS TRIGGER AS $$ +DECLARE payload text; +BEGIN +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.id AS text) INTO payload; +PERFORM pg_notify(CAST('specification_update' AS text), payload); +END IF; +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_specification_UPDATE AFTER UPDATE ON resource_allocation.specification FOR EACH ROW -EXECUTE PROCEDURE resource_allocation.NOTIFY_specification_UPDATE_with_task_view(); +EXECUTE PROCEDURE resource_allocation.NOTIFY_specification_UPDATE(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_claim_INSERT_with_resource_claim_view ON resource_allocation.resource_claim CASCADE; -DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_resource_claim_INSERT_with_resource_claim_view(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_claim_INSERT ON resource_allocation.resource_claim CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_resource_claim_INSERT(); -CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_resource_claim_INSERT_with_resource_claim_view() +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_resource_claim_INSERT() RETURNS TRIGGER AS $$ -DECLARE -new_row_from_view resource_allocation.resource_claim_view%ROWTYPE; +DECLARE payload text; BEGIN -select * into new_row_from_view from resource_allocation.resource_claim_view where id = NEW.id LIMIT 1; -PERFORM pg_notify(CAST('resource_claim_insert_with_resource_claim_view' AS text), -'{"old":' || 'null' || ',"new":' || row_to_json(new_row_from_view)::text || '}'); +SELECT CAST(NEW.id AS text) INTO payload; +PERFORM pg_notify(CAST('resource_claim_insert' AS text), payload); RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_claim_INSERT_with_resource_claim_view +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_claim_INSERT AFTER INSERT ON resource_allocation.resource_claim FOR EACH ROW -EXECUTE PROCEDURE resource_allocation.NOTIFY_resource_claim_INSERT_with_resource_claim_view(); +EXECUTE PROCEDURE resource_allocation.NOTIFY_resource_claim_INSERT(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_claim_UPDATE_with_resource_claim_view ON resource_allocation.resource_claim CASCADE; -DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_resource_claim_UPDATE_with_resource_claim_view(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_claim_UPDATE ON resource_allocation.resource_claim CASCADE; +DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_resource_claim_UPDATE(); -CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_resource_claim_UPDATE_with_resource_claim_view() +CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_resource_claim_UPDATE() RETURNS TRIGGER AS $$ -DECLARE -new_row_from_view resource_allocation.resource_claim_view%ROWTYPE; +DECLARE payload text; BEGIN -select * into new_row_from_view from resource_allocation.resource_claim_view where id = NEW.id LIMIT 1; -PERFORM pg_notify(CAST('resource_claim_update_with_resource_claim_view' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || row_to_json(new_row_from_view)::text || '}'); +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.id AS text) INTO payload; +PERFORM pg_notify(CAST('resource_claim_update' AS text), payload); +END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_claim_UPDATE_with_resource_claim_view +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_claim_UPDATE AFTER UPDATE ON resource_allocation.resource_claim FOR EACH ROW -EXECUTE PROCEDURE resource_allocation.NOTIFY_resource_claim_UPDATE_with_resource_claim_view(); +EXECUTE PROCEDURE resource_allocation.NOTIFY_resource_claim_UPDATE(); DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_claim_DELETE ON resource_allocation.resource_claim CASCADE; @@ -147,9 +274,10 @@ DROP FUNCTION IF EXISTS resource_allocation.NOTIFY_resource_claim_DELETE(); CREATE OR REPLACE FUNCTION resource_allocation.NOTIFY_resource_claim_DELETE() RETURNS TRIGGER AS $$ +DECLARE payload text; BEGIN -PERFORM pg_notify(CAST('resource_claim_delete' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || 'null' || '}'); +SELECT CAST(OLD.id AS text) INTO payload; +PERFORM pg_notify(CAST('resource_claim_delete' AS text), payload); RETURN OLD; END; $$ LANGUAGE plpgsql; @@ -161,42 +289,48 @@ FOR EACH ROW EXECUTE PROCEDURE resource_allocation.NOTIFY_resource_claim_DELETE(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_availability_UPDATE ON resource_monitoring.resource_availability CASCADE; -DROP FUNCTION IF EXISTS resource_monitoring.NOTIFY_resource_availability_UPDATE(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_availability_UPDATE_column_resource_id ON resource_monitoring.resource_availability CASCADE; +DROP FUNCTION IF EXISTS resource_monitoring.NOTIFY_resource_availability_UPDATE_column_resource_id(); -CREATE OR REPLACE FUNCTION resource_monitoring.NOTIFY_resource_availability_UPDATE() +CREATE OR REPLACE FUNCTION resource_monitoring.NOTIFY_resource_availability_UPDATE_column_resource_id() RETURNS TRIGGER AS $$ +DECLARE payload text; BEGIN -PERFORM pg_notify(CAST('resource_availability_update' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || row_to_json(NEW)::text || '}'); +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.resource_id AS text) INTO payload; +PERFORM pg_notify(CAST('resource_availability_update_column_resource_id' AS text), payload); +END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_availability_UPDATE +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_availability_UPDATE_column_resource_id AFTER UPDATE ON resource_monitoring.resource_availability FOR EACH ROW -EXECUTE PROCEDURE resource_monitoring.NOTIFY_resource_availability_UPDATE(); +EXECUTE PROCEDURE resource_monitoring.NOTIFY_resource_availability_UPDATE_column_resource_id(); -DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_capacity_UPDATE ON resource_monitoring.resource_capacity CASCADE; -DROP FUNCTION IF EXISTS resource_monitoring.NOTIFY_resource_capacity_UPDATE(); +DROP TRIGGER IF EXISTS TRIGGER_NOTIFY_NOTIFY_resource_capacity_UPDATE_column_resource_id ON resource_monitoring.resource_capacity CASCADE; +DROP FUNCTION IF EXISTS resource_monitoring.NOTIFY_resource_capacity_UPDATE_column_resource_id(); -CREATE OR REPLACE FUNCTION resource_monitoring.NOTIFY_resource_capacity_UPDATE() +CREATE OR REPLACE FUNCTION resource_monitoring.NOTIFY_resource_capacity_UPDATE_column_resource_id() RETURNS TRIGGER AS $$ +DECLARE payload text; BEGIN -PERFORM pg_notify(CAST('resource_capacity_update' AS text), -'{"old":' || row_to_json(OLD)::text || ',"new":' || row_to_json(NEW)::text || '}'); +IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN +SELECT CAST(NEW.resource_id AS text) INTO payload; +PERFORM pg_notify(CAST('resource_capacity_update_column_resource_id' AS text), payload); +END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_capacity_UPDATE +CREATE TRIGGER TRIGGER_NOTIFY_NOTIFY_resource_capacity_UPDATE_column_resource_id AFTER UPDATE ON resource_monitoring.resource_capacity FOR EACH ROW -EXECUTE PROCEDURE resource_monitoring.NOTIFY_resource_capacity_UPDATE(); +EXECUTE PROCEDURE resource_monitoring.NOTIFY_resource_capacity_UPDATE_column_resource_id(); diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_resource_allocation_statics.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_resource_allocation_statics.sql index 4717ae0102844187eb069ff0000155a397c87008..ef70cae296b120a236412214e92a01a671802f79 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_resource_allocation_statics.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_resource_allocation_statics.sql @@ -7,8 +7,9 @@ INSERT INTO resource_allocation.task_status VALUES (200, 'prepared'), (300, 'app (1150, 'error'), (1200, 'obsolete'); -- This is the list from OTDB, we'll need to merge it with the list from MoM in the future, might use different indexes? INSERT INTO resource_allocation.task_type VALUES (0, 'observation'),(1, 'pipeline'); -- We'll need more types INSERT INTO resource_allocation.resource_claim_status VALUES (0, 'claimed'), (1, 'allocated'), (2, 'conflict'); -INSERT INTO resource_allocation.resource_claim_property_type VALUES (0, 'nr_of_is_files'),(1, 'nr_of_cs_files'),(2, 'nr_of_uv_files'),(3, 'nr_of_im_files'),(4, 'nr_of_img_files'),(5, 'nr_of_pulp_files'),(6, 'nr_of_cs_stokes'),(7, 'nr_of_is_stokes'),(8, 'is_file_size'),(9, 'cs_file_size'),(10, 'uv_file_size'),(11, 'im_file_size'),(12, 'img_file_size'),(13, 'nr_of_pulp_files'),(14, 'nr_of_tabs'); -INSERT INTO resource_allocation.config VALUES (0, 'max_fill_percentage_cep4', '85.00'), (1, 'claim_timeout', '172800'); -- Just some values 172800 is two days in seconds +INSERT INTO resource_allocation.resource_claim_property_type VALUES (0, 'nr_of_is_files'),(1, 'nr_of_cs_files'),(2, 'nr_of_uv_files'),(3, 'nr_of_im_files'),(4, 'nr_of_img_files'),(5, 'nr_of_pulp_files'),(6, 'nr_of_cs_stokes'),(7, 'nr_of_is_stokes'),(8, 'is_file_size'),(9, 'cs_file_size'),(10, 'uv_file_size'),(11, 'im_file_size'),(12, 'img_file_size'),(13, 'nr_of_pulp_files'),(14, 'nr_of_tabs'),(15, 'start_sb_nr'),(16,'uv_otdb_id'),(17,'cs_otdb_id'),(18,'is_otdb_id'),(19,'im_otdb_id'),(20,'img_otdb_id'),(21,'pulp_otdb_id'),(22, 'is_tab_nr'),(23, 'start_sbg_nr'); +INSERT INTO resource_allocation.resource_claim_property_io_type VALUES (0, 'output'),(1, 'input'); +INSERT INTO resource_allocation.config VALUES (0, 'max_fill_percentage_cep4', '85.00'), (1, 'claim_timeout', '172800'), (2, 'min_inter_task_delay', '60'); -- Just some values 172800 is two days in seconds INSERT INTO resource_allocation.conflict_reason VALUES (1, 'Not enough total free storage space'), diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_triggers.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_triggers.sql index d8df06ab62377c8ca80c3a282eb08a0f5a7e6b89..bb754b359b9e2b82ec215d70457d1b4784267f64 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_triggers.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_triggers.sql @@ -143,3 +143,92 @@ CREATE TRIGGER trigger_before_insert_conflict_reason_do_task_status_check EXECUTE PROCEDURE resource_allocation.before_insert_conflict_reason_do_task_status_check(); --------------------------------------------------------------------------------------------------------------------- + +DROP TRIGGER IF EXISTS trigger_specification_insertupdate_check_startendtimes ON resource_allocation.specification; +DROP FUNCTION IF EXISTS resource_allocation.on_insertupdate_check_specification_startendtimes(); + +CREATE OR REPLACE FUNCTION resource_allocation.on_insertupdate_check_specification_startendtimes() + RETURNS trigger AS +$BODY$ +DECLARE +task RECORD; +pred_task RECORD; +suc_task RECORD; +predecessor_task_id int; +successor_task_id int; +moved_seconds double precision; +duration double precision; +max_pred_endtime timestamp := '1900-01-01 00:00:00'; +tmp_time timestamp; +min_starttime timestamp; +min_inter_task_delay int; +BEGIN + --swap start/end time if needed + IF NEW.starttime > NEW.endtime THEN + RAISE NOTICE 'NEW.starttime > NEW.endtime'; + tmp_time := NEW.starttime; + NEW.starttime := NEW.endtime; + NEW.endtime := tmp_time; + END IF; + + --store task duration + SELECT EXTRACT(epoch FROM age(NEW.endtime, NEW.starttime)) INTO duration; + + --deterimine max_pred_endtime + FOR task IN SELECT * FROM resource_allocation.task_view tv WHERE tv.specification_id = NEW.id LOOP + IF task.predecessor_ids IS NOT NULL THEN + FOREACH predecessor_task_id IN ARRAY task.predecessor_ids LOOP + FOR pred_task IN SELECT * FROM resource_allocation.task_view tv WHERE tv.id = predecessor_task_id LOOP + IF pred_task.endtime > max_pred_endtime THEN + max_pred_endtime := pred_task.endtime; + END IF; + END LOOP; + END LOOP; + END IF; + END LOOP; + + --check if spec is before max_pred_endtime, correct if needed. + IF max_pred_endtime > '1900-01-01 00:00:00' THEN + SELECT c.value::integer INTO min_inter_task_delay FROM resource_allocation.config c WHERE c.name = 'min_inter_task_delay'; + IF min_inter_task_delay IS NULL THEN + min_inter_task_delay := 0; + END IF; + min_starttime := max_pred_endtime + min_inter_task_delay * interval '1 second'; + IF min_starttime > NEW.starttime THEN + NEW.starttime := min_starttime; + NEW.endtime := min_starttime + duration * interval '1 second'; + END IF; + END IF; + + --move successor tasks by same amount if needed + IF TG_OP = 'UPDATE' THEN + IF NEW.endtime <> OLD.endtime THEN + SELECT EXTRACT(epoch FROM age(NEW.endtime, OLD.endtime)) INTO moved_seconds; + FOR task IN SELECT * FROM resource_allocation.task_view tv WHERE tv.specification_id = NEW.id LOOP + IF task.successor_ids IS NOT NULL THEN + FOREACH successor_task_id IN ARRAY task.successor_ids LOOP + FOR suc_task IN SELECT * FROM resource_allocation.task_view tv WHERE tv.id = successor_task_id LOOP + UPDATE resource_allocation.specification SET (starttime, endtime) = (starttime + moved_seconds * interval '1 second', endtime + moved_seconds * interval '1 second') WHERE id = suc_task.specification_id; + END LOOP; + END LOOP; + END IF; + END LOOP; + END IF; + END IF; + +RETURN NEW; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE + COST 100; +ALTER FUNCTION resource_allocation.on_insertupdate_check_specification_startendtimes() + OWNER TO resourceassignment; + +CREATE TRIGGER trigger_specification_insertupdate_check_startendtimes + BEFORE INSERT OR UPDATE + ON resource_allocation.specification + FOR EACH ROW + EXECUTE PROCEDURE resource_allocation.on_insertupdate_check_specification_startendtimes(); + +--------------------------------------------------------------------------------------------------------------------- + diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_virtual_instrument.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_virtual_instrument.sql index 45e9a05943de807dad150c10e69f5771a90143a6..bceb310b1535bae134ceb86374c6f3a4eb792759 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_virtual_instrument.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/add_virtual_instrument.sql @@ -16,7 +16,7 @@ DELETE FROM virtual_instrument.unit CASCADE; -- end of initial cleanup INSERT INTO virtual_instrument.unit VALUES -(0, 'rsp_channel_bit'),(1, 'bytes'),(2, 'rcu_board'),(3, 'bytes/second'),(4, 'cores'); +(0, 'rsp_channel_bit'),(1, 'bytes'),(2, 'rcu_board'),(3, 'bits/second'),(4, 'cores'); INSERT INTO virtual_instrument.resource_type VALUES (0, 'rsp', 0), (1, 'tbb', 1), (2, 'rcu', 2), (3, 'bandwidth', 3), (4, 'processor', 4), (5, 'storage', 1); INSERT INTO virtual_instrument.resource_group_type VALUES @@ -83,7 +83,9 @@ INSERT INTO virtual_instrument.resource_group VALUES (62, 'cbt008', 5); INSERT INTO virtual_instrument.resource VALUES (0, 'bandwidth', 3), (1, 'processor', 4), (2, 'bandwidth', 3), (3, 'processor', 4), (4, 'bandwidth', 3), (5, 'processor', 4), (6, 'bandwidth', 3), (7, 'processor', 4), (8, 'bandwidth', 3), (9, 'processor', 4), (10, 'bandwidth', 3), (11, 'processor', 4), (12, 'bandwidth', 3), (13, 'processor', 4), (14, 'bandwidth', 3), (15, 'processor', 4), (16, 'bandwidth', 3), (17, 'processor', 4), (18, 'bandwidth', 3), (19, 'processor', 4), (20, 'bandwidth', 3), (21, 'processor', 4), (22, 'bandwidth', 3), (23, 'processor', 4), (24, 'bandwidth', 3), (25, 'processor', 4), (26, 'bandwidth', 3), (27, 'processor', 4), (28, 'bandwidth', 3), (29, 'processor', 4), (30, 'bandwidth', 3), (31, 'processor', 4), (32, 'bandwidth', 3), (33, 'processor', 4), (34, 'bandwidth', 3), (35, 'processor', 4), (36, 'bandwidth', 3), (37, 'processor', 4), (38, 'bandwidth', 3), (39, 'processor', 4), (40, 'bandwidth', 3), (41, 'processor', 4), (42, 'bandwidth', 3), (43, 'processor', 4), (44, 'bandwidth', 3), (45, 'processor', 4), (46, 'bandwidth', 3), (47, 'processor', 4), (48, 'bandwidth', 3), (49, 'processor', 4), (50, 'bandwidth', 3), (51, 'processor', 4), (52, 'bandwidth', 3), (53, 'processor', 4), (54, 'bandwidth', 3), (55, 'processor', 4), (56, 'bandwidth', 3), (57, 'processor', 4), (58, 'bandwidth', 3), (59, 'processor', 4), (60, 'bandwidth', 3), (61, 'processor', 4), (62, 'bandwidth', 3), (63, 'processor', 4), (64, 'bandwidth', 3), (65, 'processor', 4), (66, 'bandwidth', 3), (67, 'processor', 4), (68, 'bandwidth', 3), (69, 'processor', 4), (70, 'bandwidth', 3), (71, 'processor', 4), (72, 'bandwidth', 3), (73, 'processor', 4), (74, 'bandwidth', 3), (75, 'processor', 4), (76, 'bandwidth', 3), (77, 'processor', 4), (78, 'bandwidth', 3), (79, 'processor', 4), (80, 'bandwidth', 3), (81, 'processor', 4), (82, 'bandwidth', 3), (83, 'processor', 4), (84, 'bandwidth', 3), (85, 'processor', 4), (86, 'bandwidth', 3), (87, 'processor', 4), (88, 'bandwidth', 3), (89, 'processor', 4), (90, 'bandwidth', 3), (91, 'processor', 4), (92, 'bandwidth', 3), (93, 'processor', 4), (94, 'bandwidth', 3), (95, 'processor', 4), (96, 'bandwidth', 3), (97, 'processor', 4), (98, 'bandwidth', 3), (99, 'processor', 4), (100, 'bandwidth', 3), (101, 'processor', 4), (102, 'bandwidth', 3), (103, 'processor', 4), (104, 'bandwidth', 3), (105, 'processor', 4), (106, 'bandwidth', 3), (107, 'processor', 4), (108, 'bandwidth', 3), (109, 'processor', 4), (110, 'bandwidth', 3), (111, 'processor', 4), (112, 'bandwidth', 3), (113, 'processor', 4), (114, 'bandwidth', 3), (115, 'processor', 4), (116, 'cep4bandwidth', 3), (117, 'cep4storage', 5); INSERT INTO virtual_instrument.resource_to_resource_group VALUES (DEFAULT, 0, 5), (DEFAULT, 1, 5), (DEFAULT, 2, 6), (DEFAULT, 3, 6), (DEFAULT, 4, 7), (DEFAULT, 5, 7), (DEFAULT, 6, 8), (DEFAULT, 7, 8), (DEFAULT, 8, 9), (DEFAULT, 9, 9), (DEFAULT, 10, 10), (DEFAULT, 11, 10), (DEFAULT, 12, 11), (DEFAULT, 13, 11), (DEFAULT, 14, 12), (DEFAULT, 15, 12), (DEFAULT, 16, 13), (DEFAULT, 17, 13), (DEFAULT, 18, 14), (DEFAULT, 19, 14), (DEFAULT, 20, 15), (DEFAULT, 21, 15), (DEFAULT, 22, 16), (DEFAULT, 23, 16), (DEFAULT, 24, 17), (DEFAULT, 25, 17), (DEFAULT, 26, 18), (DEFAULT, 27, 18), (DEFAULT, 28, 19), (DEFAULT, 29, 19), (DEFAULT, 30, 20), (DEFAULT, 31, 20), (DEFAULT, 32, 21), (DEFAULT, 33, 21), (DEFAULT, 34, 22), (DEFAULT, 35, 22), (DEFAULT, 36, 23), (DEFAULT, 37, 23), (DEFAULT, 38, 24), (DEFAULT, 39, 24), (DEFAULT, 40, 25), (DEFAULT, 41, 25), (DEFAULT, 42, 26), (DEFAULT, 43, 26), (DEFAULT, 44, 27), (DEFAULT, 45, 27), (DEFAULT, 46, 28), (DEFAULT, 47, 28), (DEFAULT, 48, 29), (DEFAULT, 49, 29), (DEFAULT, 50, 30), (DEFAULT, 51, 30), (DEFAULT, 52, 31), (DEFAULT, 53, 31), (DEFAULT, 54, 32), (DEFAULT, 55, 32), (DEFAULT, 56, 33), (DEFAULT, 57, 33), (DEFAULT, 58, 34), (DEFAULT, 59, 34), (DEFAULT, 60, 35), (DEFAULT, 61, 35), (DEFAULT, 62, 36), (DEFAULT, 63, 36), (DEFAULT, 64, 37), (DEFAULT, 65, 37), (DEFAULT, 66, 38), (DEFAULT, 67, 38), (DEFAULT, 68, 39), (DEFAULT, 69, 39), (DEFAULT, 70, 40), (DEFAULT, 71, 40), (DEFAULT, 72, 41), (DEFAULT, 73, 41), (DEFAULT, 74, 42), (DEFAULT, 75, 42), (DEFAULT, 76, 43), (DEFAULT, 77, 43), (DEFAULT, 78, 44), (DEFAULT, 79, 44), (DEFAULT, 80, 45), (DEFAULT, 81, 45), (DEFAULT, 82, 46), (DEFAULT, 83, 46), (DEFAULT, 84, 47), (DEFAULT, 85, 47), (DEFAULT, 86, 48), (DEFAULT, 87, 48), (DEFAULT, 88, 49), (DEFAULT, 89, 49), (DEFAULT, 90, 50), (DEFAULT, 91, 50), (DEFAULT, 92, 51), (DEFAULT, 93, 51), (DEFAULT, 94, 52), (DEFAULT, 95, 52), (DEFAULT, 96, 53), (DEFAULT, 97, 53), (DEFAULT, 98, 54), (DEFAULT, 99, 54), (DEFAULT, 100, 55), (DEFAULT, 101, 55), (DEFAULT, 102, 56), (DEFAULT, 103, 56), (DEFAULT, 104, 57), (DEFAULT, 105, 57), (DEFAULT, 106, 58), (DEFAULT, 107, 58), (DEFAULT, 108, 59), (DEFAULT, 109, 59), (DEFAULT, 110, 60), (DEFAULT, 111, 60), (DEFAULT, 112, 61), (DEFAULT, 113, 61), (DEFAULT, 114, 62), (DEFAULT, 115, 62), (DEFAULT, 116, 1), (DEFAULT, 117, 1); -INSERT INTO resource_monitoring.resource_capacity VALUES (DEFAULT, 0, 53687091200, 53687091200), (DEFAULT, 1, 24, 24), (DEFAULT, 2, 53687091200, 53687091200), (DEFAULT, 3, 24, 24), (DEFAULT, 4, 53687091200, 53687091200), (DEFAULT, 5, 24, 24), (DEFAULT, 6, 53687091200, 53687091200), (DEFAULT, 7, 24, 24), (DEFAULT, 8, 53687091200, 53687091200), (DEFAULT, 9, 24, 24), (DEFAULT, 10, 53687091200, 53687091200), (DEFAULT, 11, 24, 24), (DEFAULT, 12, 53687091200, 53687091200), (DEFAULT, 13, 24, 24), (DEFAULT, 14, 53687091200, 53687091200), (DEFAULT, 15, 24, 24), (DEFAULT, 16, 53687091200, 53687091200), (DEFAULT, 17, 24, 24), (DEFAULT, 18, 53687091200, 53687091200), (DEFAULT, 19, 24, 24), (DEFAULT, 20, 53687091200, 53687091200), (DEFAULT, 21, 24, 24), (DEFAULT, 22, 53687091200, 53687091200), (DEFAULT, 23, 24, 24), (DEFAULT, 24, 53687091200, 53687091200), (DEFAULT, 25, 24, 24), (DEFAULT, 26, 53687091200, 53687091200), (DEFAULT, 27, 24, 24), (DEFAULT, 28, 53687091200, 53687091200), (DEFAULT, 29, 24, 24), (DEFAULT, 30, 53687091200, 53687091200), (DEFAULT, 31, 24, 24), (DEFAULT, 32, 53687091200, 53687091200), (DEFAULT, 33, 24, 24), (DEFAULT, 34, 53687091200, 53687091200), (DEFAULT, 35, 24, 24), (DEFAULT, 36, 53687091200, 53687091200), (DEFAULT, 37, 24, 24), (DEFAULT, 38, 53687091200, 53687091200), (DEFAULT, 39, 24, 24), (DEFAULT, 40, 53687091200, 53687091200), (DEFAULT, 41, 24, 24), (DEFAULT, 42, 53687091200, 53687091200), (DEFAULT, 43, 24, 24), (DEFAULT, 44, 53687091200, 53687091200), (DEFAULT, 45, 24, 24), (DEFAULT, 46, 53687091200, 53687091200), (DEFAULT, 47, 24, 24), (DEFAULT, 48, 53687091200, 53687091200), (DEFAULT, 49, 24, 24), (DEFAULT, 50, 53687091200, 53687091200), (DEFAULT, 51, 24, 24), (DEFAULT, 52, 53687091200, 53687091200), (DEFAULT, 53, 24, 24), (DEFAULT, 54, 53687091200, 53687091200), (DEFAULT, 55, 24, 24), (DEFAULT, 56, 53687091200, 53687091200), (DEFAULT, 57, 24, 24), (DEFAULT, 58, 53687091200, 53687091200), (DEFAULT, 59, 24, 24), (DEFAULT, 60, 53687091200, 53687091200), (DEFAULT, 61, 24, 24), (DEFAULT, 62, 53687091200, 53687091200), (DEFAULT, 63, 24, 24), (DEFAULT, 64, 53687091200, 53687091200), (DEFAULT, 65, 24, 24), (DEFAULT, 66, 53687091200, 53687091200), (DEFAULT, 67, 24, 24), (DEFAULT, 68, 53687091200, 53687091200), (DEFAULT, 69, 24, 24), (DEFAULT, 70, 53687091200, 53687091200), (DEFAULT, 71, 24, 24), (DEFAULT, 72, 53687091200, 53687091200), (DEFAULT, 73, 24, 24), (DEFAULT, 74, 53687091200, 53687091200), (DEFAULT, 75, 24, 24), (DEFAULT, 76, 53687091200, 53687091200), (DEFAULT, 77, 24, 24), (DEFAULT, 78, 53687091200, 53687091200), (DEFAULT, 79, 24, 24), (DEFAULT, 80, 53687091200, 53687091200), (DEFAULT, 81, 24, 24), (DEFAULT, 82, 53687091200, 53687091200), (DEFAULT, 83, 24, 24), (DEFAULT, 84, 53687091200, 53687091200), (DEFAULT, 85, 24, 24), (DEFAULT, 86, 53687091200, 53687091200), (DEFAULT, 87, 24, 24), (DEFAULT, 88, 53687091200, 53687091200), (DEFAULT, 89, 24, 24), (DEFAULT, 90, 53687091200, 53687091200), (DEFAULT, 91, 24, 24), (DEFAULT, 92, 53687091200, 53687091200), (DEFAULT, 93, 24, 24), (DEFAULT, 94, 53687091200, 53687091200), (DEFAULT, 95, 24, 24), (DEFAULT, 96, 53687091200, 53687091200), (DEFAULT, 97, 24, 24), (DEFAULT, 98, 53687091200, 53687091200), (DEFAULT, 99, 24, 24), (DEFAULT, 100, 53687091200, 53687091200), (DEFAULT, 101, 24, 24), (DEFAULT, 102, 53687091200, 53687091200), (DEFAULT, 103, 24, 24), (DEFAULT, 104, 53687091200, 53687091200), (DEFAULT, 105, 24, 24), (DEFAULT, 106, 53687091200, 53687091200), (DEFAULT, 107, 24, 24), (DEFAULT, 108, 53687091200, 53687091200), (DEFAULT, 109, 24, 24), (DEFAULT, 110, 53687091200, 53687091200), (DEFAULT, 111, 24, 24), (DEFAULT, 112, 53687091200, 53687091200), (DEFAULT, 113, 24, 24), (DEFAULT, 114, 53687091200, 53687091200), (DEFAULT, 115, 24, 24), (DEFAULT, 116, 85899345920, 85899345920), (DEFAULT, 117, 2254857830400, 2254857830400); +INSERT INTO resource_monitoring.resource_capacity VALUES (DEFAULT, 0, 50000000000, 50000000000), (DEFAULT, 1, 24, 24), (DEFAULT, 2, 50000000000, 50000000000), (DEFAULT, 3, 24, 24), (DEFAULT, 4, 50000000000, 50000000000), (DEFAULT, 5, 24, 24), (DEFAULT, 6, 50000000000, 50000000000), (DEFAULT, 7, 24, 24), (DEFAULT, 8, 50000000000, 50000000000), (DEFAULT, 9, 24, 24), (DEFAULT, 10, 50000000000, 50000000000), (DEFAULT, 11, 24, 24), (DEFAULT, 12, 50000000000, 50000000000), (DEFAULT, 13, 24, 24), (DEFAULT, 14, 50000000000, 50000000000), (DEFAULT, 15, 24, 24), (DEFAULT, 16, 50000000000, 50000000000), (DEFAULT, 17, 24, 24), (DEFAULT, 18, 50000000000, 50000000000), (DEFAULT, 19, 24, 24), (DEFAULT, 20, 50000000000, 50000000000), (DEFAULT, 21, 24, 24), (DEFAULT, 22, 50000000000, 50000000000), (DEFAULT, 23, 24, 24), (DEFAULT, 24, 50000000000, 50000000000), (DEFAULT, 25, 24, 24), (DEFAULT, 26, 50000000000, 50000000000), (DEFAULT, 27, 24, 24), (DEFAULT, 28, 50000000000, 50000000000), (DEFAULT, 29, 24, 24), (DEFAULT, 30, 50000000000, 50000000000), (DEFAULT, 31, 24, 24), (DEFAULT, 32, 50000000000, 50000000000), (DEFAULT, 33, 24, 24), (DEFAULT, 34, 50000000000, 50000000000), (DEFAULT, 35, 24, 24), (DEFAULT, 36, 50000000000, 50000000000), (DEFAULT, 37, 24, 24), (DEFAULT, 38, 50000000000, 50000000000), (DEFAULT, 39, 24, 24), (DEFAULT, 40, 50000000000, 50000000000), (DEFAULT, 41, 24, 24), (DEFAULT, 42, 50000000000, 50000000000), (DEFAULT, 43, 24, 24), (DEFAULT, 44, 50000000000, 50000000000), (DEFAULT, 45, 24, 24), (DEFAULT, 46, 50000000000, 50000000000), (DEFAULT, 47, 24, 24), (DEFAULT, 48, 50000000000, 50000000000), (DEFAULT, 49, 24, 24), (DEFAULT, 50, 50000000000, 50000000000), (DEFAULT, 51, 24, 24), (DEFAULT, 52, 50000000000, 50000000000), (DEFAULT, 53, 24, 24), (DEFAULT, 54, 50000000000, 50000000000), (DEFAULT, 55, 24, 24), (DEFAULT, 56, 50000000000, 50000000000), (DEFAULT, 57, 24, 24), (DEFAULT, 58, 50000000000, 50000000000), (DEFAULT, 59, 24, 24), (DEFAULT, 60, 50000000000, 50000000000), (DEFAULT, 61, 24, 24), (DEFAULT, 62, 50000000000, 50000000000), (DEFAULT, 63, 24, 24), (DEFAULT, 64, 50000000000, 50000000000), (DEFAULT, 65, 24, 24), (DEFAULT, 66, 50000000000, 50000000000), (DEFAULT, 67, 24, 24), (DEFAULT, 68, 50000000000, 50000000000), (DEFAULT, 69, 24, 24), (DEFAULT, 70, 50000000000, 50000000000), (DEFAULT, 71, 24, 24), (DEFAULT, 72, 50000000000, 50000000000), (DEFAULT, 73, 24, 24), (DEFAULT, 74, 50000000000, 50000000000), (DEFAULT, 75, 24, 24), (DEFAULT, 76, 50000000000, 50000000000), (DEFAULT, 77, 24, 24), (DEFAULT, 78, 50000000000, 50000000000), (DEFAULT, 79, 24, 24), (DEFAULT, 80, 50000000000, 50000000000), (DEFAULT, 81, 24, 24), (DEFAULT, 82, 50000000000, 50000000000), (DEFAULT, 83, 24, 24), (DEFAULT, 84, 50000000000, 50000000000), (DEFAULT, 85, 24, 24), (DEFAULT, 86, 50000000000, 50000000000), (DEFAULT, 87, 24, 24), (DEFAULT, 88, 50000000000, 50000000000), (DEFAULT, 89, 24, 24), (DEFAULT, 90, 50000000000, 50000000000), (DEFAULT, 91, 24, 24), (DEFAULT, 92, 50000000000, 50000000000), (DEFAULT, 93, 24, 24), (DEFAULT, 94, 50000000000, 50000000000), (DEFAULT, 95, 24, 24), (DEFAULT, 96, 50000000000, 50000000000), (DEFAULT, 97, 24, 24), (DEFAULT, 98, 50000000000, 50000000000), (DEFAULT, 99, 24, 24), (DEFAULT, 100, 50000000000, 50000000000), (DEFAULT, 101, 24, 24), (DEFAULT, 102, 50000000000, 50000000000), (DEFAULT, 103, 24, 24), (DEFAULT, 104, 50000000000, 50000000000), (DEFAULT, 105, 24, 24), (DEFAULT, 106, 50000000000, 50000000000), (DEFAULT, 107, 24, 24), (DEFAULT, 108, 50000000000, 50000000000), (DEFAULT, 109, 24, 24), (DEFAULT, 110, 50000000000, 50000000000), (DEFAULT, 111, 24, 24), (DEFAULT, 112, 50000000000, 50000000000), (DEFAULT, 113, 24, 24), (DEFAULT, 114, 50000000000, 50000000000), (DEFAULT, 115, 24, 24), +(DEFAULT, 116, 640000000000, 640000000000), --cep4bandwidth, guestimated from cobalt->outputproc-nodes 8*2*40Gbps +(DEFAULT, 117, 2254857830400, 2254857830400); INSERT INTO resource_monitoring.resource_availability VALUES (DEFAULT, 0, TRUE), (DEFAULT, 1, TRUE), (DEFAULT, 2, TRUE), (DEFAULT, 3, TRUE), (DEFAULT, 4, TRUE), (DEFAULT, 5, TRUE), (DEFAULT, 6, TRUE), (DEFAULT, 7, TRUE), (DEFAULT, 8, TRUE), (DEFAULT, 9, TRUE), (DEFAULT, 10, TRUE), (DEFAULT, 11, TRUE), (DEFAULT, 12, TRUE), (DEFAULT, 13, TRUE), (DEFAULT, 14, TRUE), (DEFAULT, 15, TRUE), (DEFAULT, 16, TRUE), (DEFAULT, 17, TRUE), (DEFAULT, 18, TRUE), (DEFAULT, 19, TRUE), (DEFAULT, 20, TRUE), (DEFAULT, 21, TRUE), (DEFAULT, 22, TRUE), (DEFAULT, 23, TRUE), (DEFAULT, 24, TRUE), (DEFAULT, 25, TRUE), (DEFAULT, 26, TRUE), (DEFAULT, 27, TRUE), (DEFAULT, 28, TRUE), (DEFAULT, 29, TRUE), (DEFAULT, 30, TRUE), (DEFAULT, 31, TRUE), (DEFAULT, 32, TRUE), (DEFAULT, 33, TRUE), (DEFAULT, 34, TRUE), (DEFAULT, 35, TRUE), (DEFAULT, 36, TRUE), (DEFAULT, 37, TRUE), (DEFAULT, 38, TRUE), (DEFAULT, 39, TRUE), (DEFAULT, 40, TRUE), (DEFAULT, 41, TRUE), (DEFAULT, 42, TRUE), (DEFAULT, 43, TRUE), (DEFAULT, 44, TRUE), (DEFAULT, 45, TRUE), (DEFAULT, 46, TRUE), (DEFAULT, 47, TRUE), (DEFAULT, 48, TRUE), (DEFAULT, 49, TRUE), (DEFAULT, 50, TRUE), (DEFAULT, 51, TRUE), (DEFAULT, 52, TRUE), (DEFAULT, 53, TRUE), (DEFAULT, 54, TRUE), (DEFAULT, 55, TRUE), (DEFAULT, 56, TRUE), (DEFAULT, 57, TRUE), (DEFAULT, 58, TRUE), (DEFAULT, 59, TRUE), (DEFAULT, 60, TRUE), (DEFAULT, 61, TRUE), (DEFAULT, 62, TRUE), (DEFAULT, 63, TRUE), (DEFAULT, 64, TRUE), (DEFAULT, 65, TRUE), (DEFAULT, 66, TRUE), (DEFAULT, 67, TRUE), (DEFAULT, 68, TRUE), (DEFAULT, 69, TRUE), (DEFAULT, 70, TRUE), (DEFAULT, 71, TRUE), (DEFAULT, 72, TRUE), (DEFAULT, 73, TRUE), (DEFAULT, 74, TRUE), (DEFAULT, 75, TRUE), (DEFAULT, 76, TRUE), (DEFAULT, 77, TRUE), (DEFAULT, 78, TRUE), (DEFAULT, 79, TRUE), (DEFAULT, 80, TRUE), (DEFAULT, 81, TRUE), (DEFAULT, 82, TRUE), (DEFAULT, 83, TRUE), (DEFAULT, 84, TRUE), (DEFAULT, 85, TRUE), (DEFAULT, 86, TRUE), (DEFAULT, 87, TRUE), (DEFAULT, 88, TRUE), (DEFAULT, 89, TRUE), (DEFAULT, 90, TRUE), (DEFAULT, 91, TRUE), (DEFAULT, 92, TRUE), (DEFAULT, 93, TRUE), (DEFAULT, 94, TRUE), (DEFAULT, 95, TRUE), (DEFAULT, 96, TRUE), (DEFAULT, 97, TRUE), (DEFAULT, 98, TRUE), (DEFAULT, 99, TRUE), (DEFAULT, 100, TRUE), (DEFAULT, 101, TRUE), (DEFAULT, 102, TRUE), (DEFAULT, 103, TRUE), (DEFAULT, 104, TRUE), (DEFAULT, 105, TRUE), (DEFAULT, 106, TRUE), (DEFAULT, 107, TRUE), (DEFAULT, 108, TRUE), (DEFAULT, 109, TRUE), (DEFAULT, 110, TRUE), (DEFAULT, 111, TRUE), (DEFAULT, 112, TRUE), (DEFAULT, 113, TRUE), (DEFAULT, 114, TRUE), (DEFAULT, 115, TRUE), (DEFAULT, 116, TRUE), (DEFAULT, 117, TRUE); INSERT INTO virtual_instrument.resource_group_to_resource_group VALUES (DEFAULT, 0, NULL), (DEFAULT, 3, 1), (DEFAULT, 5, 3), (DEFAULT, 6, 3), (DEFAULT, 7, 3), (DEFAULT, 8, 3), (DEFAULT, 9, 3), (DEFAULT, 10, 3), (DEFAULT, 11, 3), (DEFAULT, 12, 3), (DEFAULT, 13, 3), (DEFAULT, 14, 3), (DEFAULT, 15, 3), (DEFAULT, 16, 3), (DEFAULT, 17, 3), (DEFAULT, 18, 3), (DEFAULT, 19, 3), (DEFAULT, 20, 3), (DEFAULT, 21, 3), (DEFAULT, 22, 3), (DEFAULT, 23, 3), (DEFAULT, 24, 3), (DEFAULT, 25, 3), (DEFAULT, 26, 3), (DEFAULT, 27, 3), (DEFAULT, 28, 3), (DEFAULT, 29, 3), (DEFAULT, 30, 3), (DEFAULT, 31, 3), (DEFAULT, 32, 3), (DEFAULT, 33, 3), (DEFAULT, 34, 3), (DEFAULT, 35, 3), (DEFAULT, 36, 3), (DEFAULT, 37, 3), (DEFAULT, 38, 3), (DEFAULT, 39, 3), (DEFAULT, 40, 3), (DEFAULT, 41, 3), (DEFAULT, 42, 3), (DEFAULT, 43, 3), (DEFAULT, 44, 3), (DEFAULT, 45, 3), (DEFAULT, 46, 3), (DEFAULT, 47, 3), (DEFAULT, 48, 3), (DEFAULT, 49, 3), (DEFAULT, 50, 3), (DEFAULT, 51, 3), (DEFAULT, 52, 3), (DEFAULT, 53, 3), (DEFAULT, 54, 3), (DEFAULT, 55, 2), (DEFAULT, 56, 2), (DEFAULT, 57, 2), (DEFAULT, 58, 2), (DEFAULT, 59, 2), (DEFAULT, 60, 2), (DEFAULT, 61, 2), (DEFAULT, 62, 2), (DEFAULT, 1, 0), (DEFAULT, 2, 0); COMMIT; diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_add_notifications.sql.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_add_notifications.sql.py index 3c5234398c9dda815f1ed213231925041dedb56b..07860f3b37ce3ac55a2ebad1f4d38e2f736a8b0e 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_add_notifications.sql.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_add_notifications.sql.py @@ -40,12 +40,18 @@ if __name__ == '__main__': f.write('--this RADBPGListener then broadcasts the event on the lofar bus.\n') f.write('\n') - f.writelines(makePostgresNotificationQueries('resource_allocation', 'task', 'INSERT', view_for_row='task_view')) - f.writelines(makePostgresNotificationQueries('resource_allocation', 'task', 'UPDATE', view_for_row='task_view')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task', 'INSERT')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task', 'UPDATE')) f.writelines(makePostgresNotificationQueries('resource_allocation', 'task', 'DELETE')) - f.writelines(makePostgresNotificationQueries('resource_allocation', 'specification', 'UPDATE', view_for_row='task_view', view_selection_id='specification_id')) - f.writelines(makePostgresNotificationQueries('resource_allocation', 'resource_claim', 'INSERT', view_for_row='resource_claim_view')) - f.writelines(makePostgresNotificationQueries('resource_allocation', 'resource_claim', 'UPDATE', view_for_row='resource_claim_view')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'INSERT', column_name='task_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'UPDATE', column_name='task_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'DELETE', column_name='task_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'INSERT', column_name='predecessor_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'UPDATE', column_name='predecessor_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'task_predecessor', 'DELETE', column_name='predecessor_id')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'specification', 'UPDATE')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'resource_claim', 'INSERT')) + f.writelines(makePostgresNotificationQueries('resource_allocation', 'resource_claim', 'UPDATE')) f.writelines(makePostgresNotificationQueries('resource_allocation', 'resource_claim', 'DELETE')) - f.writelines(makePostgresNotificationQueries('resource_monitoring', 'resource_availability', 'UPDATE')) - f.writelines(makePostgresNotificationQueries('resource_monitoring', 'resource_capacity', 'UPDATE')) + f.writelines(makePostgresNotificationQueries('resource_monitoring', 'resource_availability', 'UPDATE', column_name='resource_id')) + f.writelines(makePostgresNotificationQueries('resource_monitoring', 'resource_capacity', 'UPDATE', column_name='resource_id')) diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_database.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_database.sql index 4fd10e0787527143fb0c2a9a0921dd53d32f055d..643de3fa1e0008be8e00fb1503dfa528dd50f067 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_database.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/sql/create_database.sql @@ -31,6 +31,7 @@ DROP TABLE IF EXISTS resource_monitoring.resource_availability CASCADE; DROP TABLE IF EXISTS resource_monitoring.resource_capacity CASCADE; DROP TABLE IF EXISTS resource_allocation.resource_claim_property CASCADE; DROP TABLE IF EXISTS resource_allocation.resource_claim_property_type CASCADE; +DROP TABLE IF EXISTS resource_allocation.resource_claim_property_io_type CASCADE; DROP TABLE IF EXISTS resource_allocation.sap CASCADE; DROP TABLE IF EXISTS resource_allocation.conflict_reason CASCADE; DROP TABLE IF EXISTS resource_allocation.resource_claim_conflict_reason CASCADE; @@ -134,6 +135,7 @@ CREATE TABLE resource_allocation.specification ( starttime timestamp, endtime timestamp, content text, + cluster text DEFAULT '', PRIMARY KEY (id) ) WITH (OIDS=FALSE); ALTER TABLE resource_allocation.specification @@ -141,8 +143,8 @@ ALTER TABLE resource_allocation.specification CREATE TABLE resource_allocation.task ( id serial NOT NULL, - mom_id integer, - otdb_id integer, + mom_id integer UNIQUE, + otdb_id integer UNIQUE, status_id integer NOT NULL REFERENCES resource_allocation.task_status DEFERRABLE INITIALLY IMMEDIATE, type_id integer NOT NULL REFERENCES resource_allocation.task_type DEFERRABLE INITIALLY IMMEDIATE, specification_id integer NOT NULL REFERENCES resource_allocation.specification ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, @@ -155,7 +157,8 @@ CREATE TABLE resource_allocation.task_predecessor ( id serial NOT NULL, task_id integer NOT NULL REFERENCES resource_allocation.task(id) ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, predecessor_id integer NOT NULL REFERENCES resource_allocation.task(id) ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, - PRIMARY KEY (id) + PRIMARY KEY (id), + CONSTRAINT task_predecessor_unique UNIQUE (task_id, predecessor_id) ) WITH (OIDS=FALSE); ALTER TABLE resource_allocation.task_predecessor OWNER TO resourceassignment; @@ -241,11 +244,20 @@ CREATE TABLE resource_allocation.resource_claim_property_type ( ALTER TABLE resource_allocation.resource_claim_property_type OWNER TO resourceassignment; +CREATE TABLE resource_allocation.resource_claim_property_io_type ( + id serial NOT NULL, + name text NOT NULL, + PRIMARY KEY (id) +) WITH (OIDS=FALSE); +ALTER TABLE resource_allocation.resource_claim_property_io_type + OWNER TO resourceassignment; + CREATE TABLE resource_allocation.resource_claim_property ( id serial NOT NULL, resource_claim_id integer NOT NULL REFERENCES resource_allocation.resource_claim ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, sap_id integer REFERENCES resource_allocation.sap ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, type_id integer NOT NULL REFERENCES resource_allocation.resource_claim_property_type DEFERRABLE INITIALLY IMMEDIATE, + io_type_id integer NOT NULL DEFAULT 0 REFERENCES resource_allocation.resource_claim_property_io_type DEFERRABLE INITIALLY IMMEDIATE, value bigint NOT NULL DEFAULT 1, PRIMARY KEY (id) ) WITH (OIDS=FALSE); @@ -293,9 +305,14 @@ ALTER TABLE resource_allocation.config CREATE OR REPLACE VIEW resource_allocation.task_view AS SELECT t.id, t.mom_id, t.otdb_id, t.status_id, t.type_id, t.specification_id, - ts.name AS status, tt.name AS type, s.starttime, s.endtime, + ts.name AS status, tt.name AS type, s.starttime, s.endtime, extract(epoch from age(s.endtime, s.starttime)) as duration, s.cluster, (SELECT array_agg(tp.predecessor_id) FROM resource_allocation.task_predecessor tp where tp.task_id=t.id) as predecessor_ids, - (SELECT array_agg(tp.task_id) FROM resource_allocation.task_predecessor tp where tp.predecessor_id=t.id) as successor_ids + (SELECT array_agg(tp.task_id) FROM resource_allocation.task_predecessor tp where tp.predecessor_id=t.id) as successor_ids, + (SELECT DISTINCT ARRAY ( + SELECT _tp.predecessor_id + FROM resource_allocation.task_predecessor _tp, resource_allocation.task _t + WHERE t.status_id = 400 AND _tp.task_id = t.id AND _t.id = _tp.predecessor_id AND (_t.status_id = 1100 OR _t.status_id = 1150) ) as array_agg + ) as blocked_by_ids FROM resource_allocation.task t JOIN resource_allocation.task_status ts ON ts.id = t.status_id JOIN resource_allocation.task_type tt ON tt.id = t.type_id @@ -303,7 +320,7 @@ CREATE OR REPLACE VIEW resource_allocation.task_view AS ALTER VIEW resource_allocation.task_view OWNER TO resourceassignment; COMMENT ON VIEW resource_allocation.task_view - IS 'plain view on task table including task_status.name task_type.name specification.starttime and specification.endtime and the task predecessor- and successor ids'; + IS 'plain view on task table including task_status.name task_type.name specification.starttime and specification.endtime, duration in seconds, and the task predecessor- and successor ids'; CREATE OR REPLACE VIEW resource_allocation.resource_claim_view AS @@ -344,14 +361,16 @@ COMMENT ON VIEW resource_allocation.resource_claim_extended_view IS 'extended view on resource_claim table, including resource_claim_status.name and the resource itself'; CREATE OR REPLACE VIEW resource_allocation.resource_claim_property_view AS - SELECT rcp.id, rcp.resource_claim_id, rcp.value, rcp.type_id, - rcpt.name AS type_name, rcp.sap_id + SELECT rcp.id, rcp.resource_claim_id, rcp.value, rcp.sap_id, + rcp.type_id, rcpt.name AS type_name, + rcp.io_type_id, rcpiot.name AS io_type_name FROM resource_allocation.resource_claim_property rcp - JOIN resource_allocation.resource_claim_property_type rcpt ON rcpt.id = rcp.type_id; + JOIN resource_allocation.resource_claim_property_type rcpt ON rcpt.id = rcp.type_id + JOIN resource_allocation.resource_claim_property_io_type rcpiot ON rcpiot.id = rcp.io_type_id; ALTER VIEW resource_allocation.resource_claim_property_view OWNER TO resourceassignment; COMMENT ON VIEW resource_allocation.resource_claim_property_view - IS 'plain view on resource_claim_property table, including resource_claim_property_type.name'; + IS 'plain view on resource_claim_property table, including resource_claim_property_type.name and resource_claim_property_io_type.name'; CREATE OR REPLACE VIEW resource_monitoring.resource_view AS SELECT rv.*, diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssignmentEditor/CMakeLists.txt index 62f065324294a663a75ac8b8e6b547663e30fb4d..9fc5e452939e38bbe6b9ab090d48b374f0bbf1ee 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/CMakeLists.txt @@ -1,9 +1,8 @@ # $Id: CMakeLists.txt 30355 2014-11-04 13:46:05Z loose $ -lofar_package(ResourceAssignmentEditor 0.1) +lofar_package(ResourceAssignmentEditor 1.0 DEPENDS MoMQueryService ResourceAssignmentService PyMessaging DataManagement) include(PythonInstall) -set(USE_PYTHON_COMPILATION Off) add_subdirectory(lib) add_subdirectory(bin) diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice b/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice old mode 100644 new mode 100755 diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini b/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini index 80069bd27deac0a838c861ee9f24542361625013..a4bb85e8ea9ddd771739132be83ed5157fe51264 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini @@ -1,5 +1,5 @@ [program:raewebservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;raewebservice -p 7412' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec raewebservice -p 7412' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt index 0e9c1f37a64c202c611ebd336fe2ba9e102dadf2..2d2a6b7986cb9c3166e34f4b550ef33a9be5daeb 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/CMakeLists.txt @@ -4,9 +4,10 @@ python_install( __init__.py webservice.py utils.py - radbchangeshandler.py + changeshandler.py fakedata.py mom.py + storage.py DESTINATION lofar/sas/resourceassignment/resourceassignmenteditor) file(GLOB_RECURSE jquery_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/jquery/*) @@ -19,6 +20,10 @@ file(GLOB_RECURSE angular_ui_tree_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} sta file(GLOB_RECURSE angular_ui_layout_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-ui-layout/*) file(GLOB_RECURSE angular_ui_tabs_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-ui-tabs/*) file(GLOB_RECURSE angular_gantt_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-gantt/*) +file(GLOB_RECURSE angular_animate_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-animate/*) +file(GLOB_RECURSE angular_aria_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-aria/*) +file(GLOB_RECURSE angular_material_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-material/*) +file(GLOB_RECURSE angular_sanitize_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-sanitize/*) file(GLOB_RECURSE angular_moment_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/angular-moment/*) file(GLOB_RECURSE jsplumb_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/jsplumb/*) file(GLOB_RECURSE moment_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} static/js/moment/*) @@ -31,12 +36,19 @@ set(app_files static/favicon.ico static/app/app.js static/app/controllers/datacontroller.js + static/app/controllers/cleanupcontroller.js static/app/controllers/gridcontroller.js static/app/controllers/ganttresourcecontroller.js static/app/controllers/chartresourceusagecontroller.js static/app/controllers/ganttprojectcontroller.js + static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js static/css/main.css - templates/index.html) + static/icons/blocked.png + static/icons/ingest_in_progress.png + static/icons/ingest_failed.png + static/icons/ingest_successful.png + templates/index.html + templates/projects.html) set(web_files ${jquery_files} @@ -49,6 +61,10 @@ set(web_files ${angular_ui_layout_files} ${angular_ui_tabs_files} ${angular_gantt_files} + ${angular_animate_files} + ${angular_aria_files} + ${angular_material_files} + ${angular_sanitize_files} ${angular_moment_files} ${moment_files} ${jsplumb_files} diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/radbchangeshandler.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py similarity index 54% rename from SAS/ResourceAssignment/ResourceAssignmentEditor/lib/radbchangeshandler.py rename to SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py index f1d9cf31031bc1be077172ae08b7585512385c51..e1e0433fc96a6d59ae0377810de57b44b447eacc 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/radbchangeshandler.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# RADBChangesHandler.py +# ChangesHandler.py # # Copyright (C) 2015 # ASTRON (Netherlands Institute for Radio Astronomy) @@ -27,8 +27,14 @@ RADBChangesHandler listens on the lofar notification message bus and calls (empt Typical usage is to derive your own subclass from RADBChangesHandler and implement the specific on<SomeMessage> methods that you are interested in. """ -from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_BUSNAME, DEFAULT_NOTIFICATION_SUBJECTS +from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_BUSNAME as RADB_NOTIFICATION_BUSNAME +from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_SUBJECTS as RADB_NOTIFICATION_SUBJECTS from lofar.sas.resourceassignment.database.radbbuslistener import RADBBusListener + +from lofar.sas.datamanagement.common.config import DEFAULT_DM_NOTIFICATION_BUSNAME, DEFAULT_DM_NOTIFICATION_SUBJECTS +from lofar.sas.datamanagement.common.datamanagementbuslistener import DataManagementBusListener + +from lofar.common.util import humanreadablesize from lofar.common.util import waitForInterrupt from lofar.sas.resourceassignment.resourceassignmenteditor.mom import updateTaskMomDetails @@ -43,10 +49,12 @@ CHANGE_UPDATE_TYPE = 'update' CHANGE_INSERT_TYPE = 'insert' CHANGE_DELETE_TYPE = 'delete' -class RADBChangesHandler(RADBBusListener): - def __init__(self, busname=DEFAULT_NOTIFICATION_BUSNAME, subjects=DEFAULT_NOTIFICATION_SUBJECTS, broker=None, momrpc=None, **kwargs): +class ChangesHandler: + def __init__(self, radb_busname=RADB_NOTIFICATION_BUSNAME, radb_subjects=RADB_NOTIFICATION_SUBJECTS, + dm_busname=DEFAULT_DM_NOTIFICATION_BUSNAME, dm_subjects=DEFAULT_DM_NOTIFICATION_SUBJECTS, + broker=None, momqueryrpc=None, radbrpc=None, sqrpc=None, **kwargs): """ - RADBChangesHandler listens on the lofar notification message bus and keeps track of all the change notifications. + ChangesHandler listens on the lofar notification message bus and keeps track of all the change notifications. :param broker: valid Qpid broker host (default: None, which means localhost) additional parameters in kwargs: options= <dict> Dictionary of options passed to QPID @@ -54,13 +62,41 @@ class RADBChangesHandler(RADBBusListener): numthreads= <int> Number of parallel threads processing messages (default: 1) verbose= <bool> Output extra logging over stdout (default: False) """ - super(RADBChangesHandler, self).__init__(busname=busname, subjects=subjects, broker=broker, **kwargs) + self._radb_listener = RADBBusListener(busname=radb_busname, subjects=radb_subjects, broker=broker, **kwargs) + self._radb_listener.onTaskUpdated = self.onTaskUpdated + self._radb_listener.onTaskInserted = self.onTaskInserted + self._radb_listener.onTaskDeleted = self.onTaskDeleted + self._radb_listener.onResourceClaimUpdated = self.onResourceClaimUpdated + self._radb_listener.onResourceClaimInserted = self.onResourceClaimInserted + self._radb_listener.onResourceClaimDeleted = self.onResourceClaimDeleted + self._radb_listener.onResourceAvailabilityUpdated = self.onResourceAvailabilityUpdated + self._radb_listener.onResourceCapacityUpdated = self.onResourceCapacityUpdated + + self._dm_listener = DataManagementBusListener(busname=dm_busname, subjects=dm_subjects, broker=broker, **kwargs) + self._dm_listener.onDiskUsageChanged = self.onDiskUsageChanged + self._dm_listener.onTaskDeleted = self.onTaskDeletedFromDisk self._changes = [] self._lock = Lock() self._changedCondition = Condition() self._changeNumber = 0L - self._momrpc = momrpc + self._momqueryrpc = momqueryrpc + self._radbrpc = radbrpc + + def __enter__(self): + self.start_listening() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop_listening() + + def start_listening(self): + self._radb_listener.start_listening() + self._dm_listener.start_listening() + + def stop_listening(self): + self._radb_listener.stop_listening() + self._dm_listener.stop_listening() def _handleChange(self, change): '''_handleChange appends a change in the changes list and calls the onChangedCallback. @@ -76,59 +112,76 @@ class RADBChangesHandler(RADBBusListener): with self._changedCondition: self._changedCondition.notifyAll() - def onTaskUpdated(self, old_task, new_task): - '''onTaskUpdated is called upon receiving a TaskUpdated message. - :param task: dictionary with the updated task''' - new_task['starttime'] = new_task['starttime'].datetime() - new_task['endtime'] = new_task['endtime'].datetime() - task_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'task', 'value':new_task} + def onTaskUpdated(self, updated_task): + '''onTaskUpdated is called upon receiving a TaskUpdated message.''' + updated_task['starttime'] = updated_task['starttime'].datetime() + updated_task['endtime'] = updated_task['endtime'].datetime() + task_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'task', 'value':updated_task} self._handleChange(task_change) - def onTaskInserted(self, task): + def onTaskInserted(self, new_task): '''onTaskInserted is called upon receiving a TaskInserted message. - :param task: dictionary with the inserted task''' - task['starttime'] = task['starttime'].datetime() - task['endtime'] = task['endtime'].datetime() - updateTaskMomDetails(task, self._momrpc) - task_change = {'changeType':CHANGE_INSERT_TYPE, 'objectType':'task', 'value':task} + :param new_task: dictionary with the inserted task''' + new_task['starttime'] = new_task['starttime'].datetime() + new_task['endtime'] = new_task['endtime'].datetime() + updateTaskMomDetails(new_task, self._momqueryrpc) + task_change = {'changeType':CHANGE_INSERT_TYPE, 'objectType':'task', 'value':new_task} self._handleChange(task_change) - def onTaskDeleted(self, task): + def onTaskDeleted(self, old_task_id): '''onTaskDeleted is called upon receiving a TaskDeleted message. - :param task: dictionary with the deleted task''' - task_change = {'changeType':CHANGE_DELETE_TYPE, 'objectType':'task', 'value':task} + :param old_task_id: id of the deleted task''' + task_change = {'changeType':CHANGE_DELETE_TYPE, 'objectType':'task', 'value':{'id':old_task_id}} self._handleChange(task_change) - def onResourceClaimUpdated(self, old_claim, new_claim): + def onResourceClaimUpdated(self, updated_claim): '''onResourceClaimUpdated is called upon receiving a ResourceClaimUpdated message. - :param task: dictionary with the updated claim''' - new_claim['starttime'] = new_claim['starttime'].datetime() - new_claim['endtime'] = new_claim['endtime'].datetime() - claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceClaim', 'value':new_claim} + :param updated_claim: dictionary with the updated claim''' + updated_claim['starttime'] = updated_claim['starttime'].datetime() + updated_claim['endtime'] = updated_claim['endtime'].datetime() + claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceClaim', 'value':updated_claim} self._handleChange(claim_change) - def onResourceClaimInserted(self, claim): + def onResourceClaimInserted(self, new_claim): '''onResourceClaimInserted is called upon receiving a ResourceClaimInserted message. - :param claim: dictionary with the inserted claim''' - claim['starttime'] = claim['starttime'].datetime() - claim['endtime'] = claim['endtime'].datetime() - claim_change = {'changeType':CHANGE_INSERT_TYPE, 'objectType':'resourceClaim', 'value':claim} + :param new_claim: dictionary with the inserted claim''' + new_claim['starttime'] = new_claim['starttime'].datetime() + new_claim['endtime'] = new_claim['endtime'].datetime() + claim_change = {'changeType':CHANGE_INSERT_TYPE, 'objectType':'resourceClaim', 'value':new_claim} self._handleChange(claim_change) - def onResourceClaimDeleted(self, claim): + def onResourceClaimDeleted(self, old_claim_id): '''onResourceClaimDeleted is called upon receiving a ResourceClaimDeleted message. - :param claim: dictionary with the deleted claim''' - claim_change = {'changeType':CHANGE_DELETE_TYPE, 'objectType':'resourceClaim', 'value':claim} + :param old_claim_id: id of the deleted claim''' + claim_change = {'changeType':CHANGE_DELETE_TYPE, 'objectType':'resourceClaim', 'value':{'id': old_claim_id}} self._handleChange(claim_change) - def onResourceAvailabilityUpdated(self, old_availability, new_availability): - claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceAvailability', 'value':new_availability} + def onResourceAvailabilityUpdated(self, old_availability, updated_availability): + claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceAvailability', 'value':updated_availability} self._handleChange(claim_change) - def onResourceCapacityUpdated(self, old_capacity, new_capacity): - claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceCapacity', 'value':new_capacity} + def onResourceCapacityUpdated(self, old_capacity, updated_capacity): + claim_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'resourceCapacity', 'value':updated_capacity} self._handleChange(claim_change) + def _handleDiskUsageChange(self, disk_usage, otdb_id): + if otdb_id != None: + task = self._radbrpc.getTask(otdb_id=otdb_id) + if task: + du_readable = humanreadablesize(disk_usage) + logger.info('disk_usage change: otdb_id %s radb_id %s disk_usage %s %s', otdb_id, task['id'], disk_usage, du_readable) + task['disk_usage'] = disk_usage + task['disk_usage_readable'] = du_readable + + task_change = {'changeType':CHANGE_UPDATE_TYPE, 'objectType':'task', 'value':task} + self._handleChange(task_change) + + def onDiskUsageChanged(self, path, disk_usage, otdb_id): + self._handleDiskUsageChange(disk_usage, otdb_id) + + def onTaskDeletedFromDisk(self, otdb_id, paths): + self._handleDiskUsageChange(0, otdb_id) + def getMostRecentChangeNumber(self): with self._lock: if self._changes: diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py index 1390d521c887eb59a92e2159c6488efd97153a5e..909490e31b797a2c987b15f1962648833bedda2f 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py @@ -63,11 +63,53 @@ def updateTaskMomDetails(task, momrpc): t['project_name'] = m['project_name'] t['project_mom_id'] = m['project_mom2id'] t['project_mom2object_id'] = m['project_mom2objectid'] + t['description'] = m.get('object_description', '') + t['sub_type'] = m.get('object_type', t['type']).lower() t['mom2object_id'] = m['object_mom2objectid'] t['mom_object_group_id'] = m['object_group_id'] + t['mom_object_group_name'] = m.get('object_group_name') + t['mom_object_group_mom2object_id'] = m.get('object_group_mom2objectid') + t['mom_object_parent_group_id'] = m['parent_group_mom2id'] + t['mom_object_parent_group_name'] = m['parent_group_name'] else: t['project_name'] = 'OTDB Only' t['project_mom_id'] = -98 + + results = momrpc.getDataProducts(momIds) + + for t in tasklist: + mom_id = str(t['mom_id']) + t['ingest_status'] = None + t['nr_of_dataproducts'] = None + if mom_id in results: + dps = results[mom_id] + if dps != None: + t['nr_of_dataproducts'] = len(dps) + if len(dps) > 0: + num_ingested = 0 + num_ingest_pending = 0 + num_ingest_running = 0 + num_ingest_failed = 0 + num_ingest_hold = 0 + for dp in dps: + if dp['status'] == 'ingested': + num_ingested += 1 + elif dp['status'] == 'pending': + num_ingest_pending += 1 + elif dp['status'] == 'running': + num_ingest_running += 1 + elif dp['status'] == 'failed': + num_ingest_failed += 1 + elif dp['status'] == 'on_hold': + num_ingest_hold += 1 + + if num_ingested == len(dps): + t['ingest_status'] = 'ingested' + elif num_ingest_pending + num_ingest_running > 0: + t['ingest_status'] = 'ingesting' + elif num_ingest_failed + num_ingest_hold > 0: + t['ingest_status'] = 'failed' + except Exception as e: logger.error(str(e)) diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js index 1c33462df9ba33925b88f5353491b52bbf5098b8..ce05ea5a84bc136bbdabb54b8f521afe638c62a5 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/app.js @@ -1,7 +1,8 @@ // $Id$ var app = angular.module('raeApp', - ['DataControllerMod', + ['CleanupControllerMod', + 'DataControllerMod', 'GanttResourceControllerMod', 'GanttProjectControllerMod', 'ChartResourceUsageControllerMod', @@ -9,14 +10,48 @@ var app = angular.module('raeApp', 'ui.layout', 'ui.bootstrap', 'ui.bootstrap.tabs', - 'highcharts-ng']); + 'highcharts-ng', + 'ngMaterial']); app.config(['$compileProvider', function ($compileProvider) { $compileProvider.debugInfoEnabled(false); }]); +var secondsToHHmmss = function(seconds) { + var hours = Math.floor(seconds / 3600); + var remaining_seconds = seconds - (3600 * hours); + + var minutes = Math.floor(remaining_seconds / 60); + remaining_seconds = remaining_seconds - (60 * minutes); + + var str = ''; + if(hours < 10) { + str += '0'; + } + str += hours + ':'; + if(minutes < 10) { + str += '0'; + } + str += minutes + ':'; + if(remaining_seconds < 10) { + str += '0'; + } + str += remaining_seconds; + return str; +}; + app.filter('secondsToHHmmss', function($filter) { - return function(seconds) { - return $filter('date')(new Date(0, 0, 0).setSeconds(seconds), 'HH:mm:ss'); - }; + return secondsToHHmmss; }) + +//filter unique items in array +Array.prototype.unique = function() { + var unique = {}; + var length = this.length; + + for (var i = 0; i < length; i++) { + var item = this[i]; + unique[item] = true; + } + return Object.keys(unique); +}; diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js index f503b02ad280d0886a5f155aa3db17a36fa9ef37..e1c1b062cffcce852e22e1443d55b2c3670bdcee 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js @@ -61,17 +61,24 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc function updateChartLofarTime() { var lofarTime = $scope.dataService.lofarTime; - if(lofarTime.getSeconds() % 5 == 0) { - $scope.chartConfig.xAxis.plotLines = [{ - width: 3, - color: '#222222', - zIndex: 100, - value: lofarTime.getTime() - }]; - } + $scope.chartConfig.xAxis.plotLines = [{ + width: 3, + color: '#222222', + zIndex: 100, + value: lofarTime.getTime() + }]; }; - $scope.$watch('dataService.lofarTime', updateChartLofarTime); + $scope.$watch('dataService.lofarTime', function() { + var lofarTime = $scope.dataService.lofarTime; + if(lofarTime.getSeconds() % 10 == 0) { + $scope.$evalAsync(updateChartLofarTime); + } + }); + + function updateChartDataAsync() { + $scope.$evalAsync(updateChartData); + }; function updateChartData() { var selected_resource_id = $scope.dataService.selected_resource_id; @@ -237,9 +244,8 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc } }; - $scope.$watch('dataService.selected_resource_id', updateChartData); - $scope.$watch('dataService.resources', updateChartData, true); - $scope.$watch('dataService.resourceUsagesDict', updateChartData, true); - $scope.$watch('dataService.viewTimeSpan', updateChartData, true); + $scope.$watch('dataService.selected_resource_id', updateChartDataAsync); + $scope.$watch('dataService.resourceUsagesChangeCntr', updateChartDataAsync); + $scope.$watch('dataService.viewTimeSpan', updateChartDataAsync, true); } ]); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js new file mode 100644 index 0000000000000000000000000000000000000000..2dd1b99d89c633dcb0bd43421c98393d45782674 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js @@ -0,0 +1,534 @@ +// $Id: controller.js 32761 2015-11-02 11:50:21Z schaap $ + +var cleanupControllerMod = angular.module('CleanupControllerMod', ['ui.bootstrap', 'ngMaterial', 'ngSanitize']); + +cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$mdDialog', '$http', '$q', 'dataService', function($scope, $uibModal, $mdDialog, $http, $q, dataService) { + var self = this; + + self.getTaskDataPath = function(task) { + var defer = $q.defer(); + + $http.get('/rest/tasks/' + task.id + '/datapath').success(function(result) { + defer.resolve(result); + }).error(function(result) { + console.log("Error. Could get data path for task " + task.id + ", " + result); + alert(result); + }); + + return defer.promise; + }; + + self.deleteTasksDataWithConfirmation = function(tasks) { + suc_tasks_promises = []; + for(var task of tasks) { + if(task.successor_ids) { + for(var suc_id of task.successor_ids) { + suc_tasks_promises.push(dataService.getTask(suc_id)); + } + } + } + + $q.all(suc_tasks_promises).then(function(suc_tasks) { + var unfinished_suc_tasks = suc_tasks.filter(function(t) { return t && !(t.status == 'finished' || t.status == 'obsolete') }); + + if(unfinished_suc_tasks.length > 0) { + var unfinished_ids = unfinished_suc_tasks.map(function(t) { return t.id; }); + var unfinished_otdb_ids = unfinished_suc_tasks.map(function(t) { return t.otdb_id; }); + + var undeletable_predecessors = tasks.filter(function(t) { + for (var suc_id of t.successor_ids) { + if(unfinished_ids.includes(suc_id)) + return true; + } + return false; + }); + var undeletable_pred_otdb_ids = undeletable_predecessors.map(function(t) { return t.otdb_id; }); + + $mdDialog.show($mdDialog.confirm() + .parent(angular.element(document.querySelector('#popupContainer'))) + .title('Warning: Delete data which is needed by succesors?') + .htmlContent("Cannot delete data for " + undeletable_pred_otdb_ids + " because there are unfinished successors: " + unfinished_otdb_ids + "<br>Do you want to set the unfinished succesors to obsolete and proceed with the deletion of all data?") + .ariaLabel('Error') + .ok('Yes') + .cancel('No')).then(function() { + + waiting_dialog = $mdDialog.show($mdDialog.alert() + .parent(angular.element(document.querySelector('#popupContainer'))) + .title('Waiting...') + .textContent("Waiting for the unfinished succesors to become obsolete...") + .ariaLabel('Waiting') + .ok('Ok')); + + for(var unfinished_suc_task of unfinished_suc_tasks) { + var newTask = { id: unfinished_suc_task.id, status: 'obsolete' }; + dataService.putTask(newTask); + } + + var waitForTasksToBecomeObsolete = function(tasks_to_be_obsolete) { + // polling for all tasks_to_be_obsolete + // if they are all obsolete, then the returned promise resolves + // else, we poll again, and again, until they are all obsolete + var defer = $q.defer(); + var load_promises = tasks_to_be_obsolete.map(function(t) { return dataService.getTask(t.id, true); }); + + $q.all(load_promises).then(function(loaded_tasks) { + var loaded_obsolete_tasks = loaded_tasks.filter(function(t) { return t['status'] == 'obsolete';}); + if(loaded_obsolete_tasks.length == loaded_tasks.length) { + defer.resolve(); + } else { + waitForTasksToBecomeObsolete(tasks_to_be_obsolete).then(function() { + defer.resolve(); + }); + } + }); + return defer.promise; + }; + + waitForTasksToBecomeObsolete(unfinished_suc_tasks).then( + function() { + $mdDialog.cancel(waiting_dialog); + self.deleteTasksDataWithConfirmation(tasks); + }); + }, function() { + }); + return; + } + + du_promises = []; + for(var task of tasks) { + du_promises.push(dataService.getTaskDiskUsage(task)); + } + + $q.all(du_promises).then(function(du_results) { + var unfound_du_results = du_results.filter(function(r) { return !r || !r.found; }); + + if(unfound_du_results.length > 0) { + var unfound_ids = unfound_du_results.map(function(t) { return t.otdb_id; }); + $mdDialog.show($mdDialog.alert() + .parent(angular.element(document.querySelector('#popupContainer'))) + .title('Error') + .textContent("Could not find data to delete for one or more tasks") + .ariaLabel('Error') + .ok('Ok')); + return; + } + + openDeleteConfirmationDialog(du_results); + }); + }); + }; + + function deleteTaskData(task, delete_is, delete_cs, delete_uv, delete_im, delete_img, delete_pulp, delete_scratch) { + var params = {delete_is:delete_is, delete_cs:delete_cs, delete_uv:delete_uv, delete_im:delete_im, delete_img:delete_img, delete_pulp:delete_pulp, delete_scratch:delete_scratch}; + $http.delete('/rest/tasks/' + task.id + '/cleanup', {data: params}).error(function(result) { + console.log("Error. Could cleanup data for task " + task.id + ", " + result); + }).success(function(result) { + console.log(result.message); + }); + }; + + function openDeleteConfirmationDialog(du_results) { + var modalInstance = $uibModal.open({ + animation: false, + template: '<div class="modal-header">\ + <h3 class="modal-title">Are you sure?</h3>\ + </div>\ + <div class="modal-body">\ + <p>This will delete all selected data in: \ + <ul><li ng-repeat="path in paths">{{path}}</li></ul>\ + <br>Are you sure?</p>\ + <label ng-if="has_is" style="margin-left:24px">IS: {{amount_is}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_is"></label>\ + <label ng-if="has_cs" style="margin-left:24px">CS: {{amount_cs}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_cs"></label>\ + <label ng-if="has_uv" style="margin-left:24px">UV: {{amount_uv}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_uv"></label>\ + <label ng-if="has_im" style="margin-left:24px">IM: {{amount_im}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_im"></label>\ + <label ng-if="has_img" style="margin-left:24px">Images: {{amount_img}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_img"></label>\ + <label ng-if="has_pulp" style="margin-left:24px">Pulp: {{amount_pulp}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_pulp"></label>\ + <label ng-if="has_scratch" style="margin-left:24px">scratch: {{amount_scratch}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_scratch"></label>\ + </div>\ + <div class="modal-footer">\ + <button class="btn btn-primary" type="button" ng-click="ok()">OK</button>\ + <button class="btn btn-warning" type="button" autofocus ng-click="cancel()">Cancel</button>\ + </div>', + controller: function ($scope, $uibModalInstance) { + $scope.paths = du_results.map(function(r) { return r.task_directory.path; }); + $scope.has_is = false; + $scope.has_cs = false; + $scope.has_uv = false; + $scope.has_im = false; + $scope.has_img = false; + $scope.has_pulp = false; + $scope.has_scratch = false; + $scope.amount_is = 0; + $scope.amount_cs = 0; + $scope.amount_uv = 0; + $scope.amount_im = 0; + $scope.amount_img = 0; + $scope.amount_pulp = 0; + $scope.amount_scratch = 0; + + for(var du_result of du_results) { + var path = du_result.task_directory.path; + var has_is = du_result.sub_directories.hasOwnProperty(path + '/is'); + var has_cs = du_result.sub_directories.hasOwnProperty(path + '/cs'); + var has_uv = du_result.sub_directories.hasOwnProperty(path + '/uv'); + var has_im = du_result.sub_directories.hasOwnProperty(path + '/im'); + var has_img = du_result.sub_directories.hasOwnProperty(path + '/img'); + var has_pulp = du_result.sub_directories.hasOwnProperty(path + '/pulp'); + var has_scratch = du_result.task_directory.hasOwnProperty('scratch_paths'); + + $scope.has_is |= has_is; + $scope.has_cs |= has_cs; + $scope.has_uv |= has_uv; + $scope.has_im |= has_im; + $scope.has_img |= has_img; + $scope.has_pulp |= has_pulp; + $scope.has_scratch |= has_scratch; + + $scope.amount_is += has_is ? du_result.sub_directories[path + '/is'].disk_usage : 0; + $scope.amount_cs += has_cs ? du_result.sub_directories[path + '/cs'].disk_usage : 0; + $scope.amount_uv += has_uv ? du_result.sub_directories[path + '/uv'].disk_usage : 0; + $scope.amount_im += has_im ? du_result.sub_directories[path + '/im'].disk_usage : 0; + $scope.amount_img += has_img ? du_result.sub_directories[path + '/img'].disk_usage : 0; + $scope.amount_pulp += has_pulp ? du_result.sub_directories[path + '/pulp'].disk_usage : 0; + if(has_scratch) { + for(var scratch_path in du_result.task_directory.scratch_paths) { + var scratch_path_du = du_result.task_directory.scratch_paths[scratch_path]; + if(scratch_path_du.found) { + $scope.amount_scratch += scratch_path_du.disk_usage; + } + } + } + } + + $scope.amount_is = dataService.humanreadablesize($scope.amount_is); + $scope.amount_cs = dataService.humanreadablesize($scope.amount_cs); + $scope.amount_uv = dataService.humanreadablesize($scope.amount_uv); + $scope.amount_im = dataService.humanreadablesize($scope.amount_im); + $scope.amount_img = dataService.humanreadablesize($scope.amount_img); + $scope.amount_pulp = dataService.humanreadablesize($scope.amount_pulp); + $scope.amount_scratch = dataService.humanreadablesize($scope.amount_scratch); + + $scope.delete_is = true; + $scope.delete_cs = true; + $scope.delete_uv = true; + $scope.delete_im = true; + $scope.delete_img = true; + $scope.delete_pulp = true; + $scope.delete_scratch = true; + + $scope.ok = function () { + $uibModalInstance.close(); + for(var du_result of du_results) { + var task = du_result.task; + deleteTaskData(task, $scope.delete_is, $scope.delete_cs, $scope.delete_uv, $scope.delete_im, $scope.delete_img, $scope.delete_pulp, $scope.delete_scratch); + } + }; + + $scope.cancel = function () { + $uibModalInstance.dismiss('cancel'); + }; + } + }); + }; + + self.showAllProjectsDiskUsage = function() { + self.showTaskDiskUsage(undefined); + } + + self.showTaskDiskUsage = function(task) { + var modalInstance = $uibModal.open({ + animation: false, + template: '<div class="modal-header">\ + <h3 class="modal-title">Disk usage</h3>\ + </div>\ + <div class="modal-body" style="text-align:right">\ + <highchart id="chart_total_disk_usage" config="totalDiskUsageChartConfig" style="width: 960px; height: 120px; margin-bottom: 20px;" ></highchart>\ + <hr>\ + <highchart id="chart_disk_usage" config="diskUsageChartConfig" style="width: 960px; height: 720px;" ></highchart>\ + <p>\ + <span style="margin-right:50px">Last updated at: {{leastRecentCacheTimestamp | date }}</span>\ + <button class="btn btn-primary glyphicon glyphicon-level-up" type="button" ng-click="up()" title="Up one level" ng-if="watchedObjectType!=\'projects\'"></button>\ + </p>\ + </div>\ + <div class="modal-footer">\ + <button class="btn btn-primary" type="button" autofocus ng-click="ok()">OK</button>\ + </div>', + controller: function ($scope, $uibModalInstance) { + $scope.ok = function () { + $uibModalInstance.close(); + }; + + const OBJECT_TYPE_TASK = 'task'; + const OBJECT_TYPE_TASKS = 'tasks'; + const OBJECT_TYPE_PROJECT = 'project'; + const OBJECT_TYPE_PROJECTS = 'projects'; + $scope.watchedObjectType = OBJECT_TYPE_TASK; + + $scope.leastRecentCacheTimestamp = ''; + + $scope.onPieClicked = function(event) { + switch($scope.watchedObjectType) { + case OBJECT_TYPE_TASKS: + case OBJECT_TYPE_PROJECT: + loadTaskDiskUsage(this.otdb_id); + break; + case OBJECT_TYPE_PROJECTS: + loadProjectDiskUsage(this.project_name); + break; + } + }; + + $scope.up = function () { + switch($scope.watchedObjectType) { + case OBJECT_TYPE_TASK: + loadProjectDiskUsage($scope.diskUsageChartSeries[0].project_name); + break; + case OBJECT_TYPE_TASKS: + case OBJECT_TYPE_PROJECT: + loadAllProjectsDiskUsage(); + break; + } + }; + + $scope.diskUsageChartSeries = [{name:'Loading data...', data:[]}]; + + $scope.diskUsageChartConfig = { + options: { + chart: { + type: 'pie', + animation: { + duration: 200 + }, + legend: { + enabled: false + } + }, + legend: { + enabled: false + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: true + }, + showInLegend: false + }, + series: { + point: { + events: { + click: $scope.onPieClicked + } + } + } + }, + tooltip: { + headerFormat: '{series.name}<br/>', + pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' + } + }, + series: $scope.diskUsageChartSeries, + title: { + text: 'Loading data...' + }, + credits: { + enabled: false + }, + loading: false + } + + $scope.totalDiskUsageChartSeries = []; + + var cep4storage_resource = dataService.resources.find(function(r) { return r.name == 'cep4storage'; }); + if(cep4storage_resource) { + $scope.totalDiskUsageChartSeries = [{name:'Free', data:[100.0*cep4storage_resource.available_capacity/cep4storage_resource.total_capacity], color:'#a3f75c'}, + {name:'Used', data:[100.0*cep4storage_resource.used_capacity/cep4storage_resource.total_capacity], color:'#f45b5b'}]; + } + + $scope.totalDiskUsageChartConfig = { + options: { + chart: { + type: 'bar', + animation: { + duration: 200 + }, + legend: { + enabled: false + } + }, + navigation: { + buttonOptions: { + enabled: false + } + + }, + plotOptions: { + bar: { + allowPointSelect: false, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: false, + }, + series: { + stacking: 'normal', + pointWidth: 32 + }, + }, + yAxis: { + visible: true, + title: {text:'Percentage'}, + min: 0, + max: 100, + endOnTick: false + }, + xAxis: { + visible: false + }, + tooltip: { + headerFormat: '{series.name}<br/>', + pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' + }, + }, + series: $scope.totalDiskUsageChartSeries, + title: { + text: 'CEP4 total disk usage' + }, + credits: { + enabled: false + }, + loading: false + } + + var loadTaskDiskUsage = function(otdb_id) { + dataService.getTaskDiskUsageByOTDBId(otdb_id).then(function(result) { + if(result.found) { + $scope.watchedObjectType = OBJECT_TYPE_TASK; + $scope.diskUsageChartConfig.title.text = result.task_directory.name + ' ' + result.task_directory.disk_usage_readable; + $scope.diskUsageChartSeries[0].name = $scope.diskUsageChartConfig.title.text; + var path_parts = result.task_directory.path.split('/'); + $scope.diskUsageChartSeries[0].project_name = path_parts[path_parts.length-2]; + $scope.diskUsageChartSeries[0].data.splice(0, $scope.diskUsageChartSeries[0].data.length); + $scope.leastRecentCacheTimestamp = result.task_directory.cache_timestamp; + + var sub_directory_names = Object.keys(result.sub_directories); + sub_directory_names.sort(function(a, b) { return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0)); }); + for(var sub_dir of sub_directory_names) { + var sub_dir_result = result.sub_directories[sub_dir]; + $scope.diskUsageChartSeries[0].data.push({name:sub_dir_result.name + ' ' + sub_dir_result.disk_usage_readable,y:sub_dir_result.disk_usage || 0}); + + if(sub_dir_result.cache_timestamp < $scope.leastRecentCacheTimestamp) { + $scope.leastRecentCacheTimestamp = sub_dir_result.cache_timestamp; + } + } + $scope.leastRecentCacheTimestamp = dataService.convertDatestringToLocalUTCDate($scope.leastRecentCacheTimestamp); + }else { + $scope.ok(); + $scope.$evalAsync(function() { alert("Could not find disk usage for task " + otdb_id); }); + } + }); + }; + + var loadTasksDiskUsage = function(tasks) { + var du_promises = tasks.map(function(t) { return dataService.getTaskDiskUsageByOTDBId(t.otdb_id); }); + $q.all(du_promises).then(function(du_results) { + var found_task_dus = du_results.filter(function(r) { return r.found;}).map(function(r) { return r.task_directory; }); + + if(found_task_dus.length > 0) { + $scope.watchedObjectType = OBJECT_TYPE_TASKS; + + $scope.leastRecentCacheTimestamp = new Date(Math.min.apply(null, found_task_dus.map(function(tdu) { return dataService.convertDatestringToLocalUTCDate(tdu.cache_timestamp); }))); + + var total_usage = found_task_dus.map(function(tdu) { return tdu.disk_usage; }).reduce(function(a, b) { return a + b;}); + var total_usage_readable = dataService.humanreadablesize(total_usage); + + $scope.diskUsageChartConfig.title.text = 'Total size: ' + total_usage_readable; + $scope.diskUsageChartSeries[0].data.splice(0, $scope.diskUsageChartSeries[0].data.length); + $scope.diskUsageChartSeries[0].name = $scope.diskUsageChartConfig.title.text; + + for(var task_du of found_task_dus) { + $scope.diskUsageChartSeries[0].data.push({name:task_du.name + ' ' + task_du.disk_usage_readable, + y:task_du.disk_usage || 0, + otdb_id: task_du.otdb_id }); + } + }else { + $scope.ok(); + $scope.$evalAsync(function() { alert("Could not find disk usage for task " + otdb_id); }); + } + }); + }; + + var loadProjectDiskUsage = function(project_name) { + dataService.getProjectDiskUsage(project_name).then(function(result) { + if(result.found) { + $scope.watchedObjectType = OBJECT_TYPE_PROJECT; + $scope.diskUsageChartConfig.title.text = result.projectdir.name + ' ' + result.projectdir.disk_usage_readable; + $scope.diskUsageChartSeries[0].name = $scope.diskUsageChartConfig.title.text; + $scope.diskUsageChartSeries[0].data.splice(0, $scope.diskUsageChartSeries[0].data.length); + $scope.leastRecentCacheTimestamp = result.projectdir.cache_timestamp; + + var sub_directory_names = Object.keys(result.sub_directories); + sub_directory_names.sort(function(a, b) { return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0)); }); + for(var sub_dir of sub_directory_names) { + var sub_dir_result = result.sub_directories[sub_dir]; + $scope.diskUsageChartSeries[0].data.push({name:sub_dir_result.name + ' ' + sub_dir_result.disk_usage_readable, + y:sub_dir_result.disk_usage || 0, + otdb_id: parseInt(sub_dir_result.name.slice(1)) }); + + if(sub_dir_result.cache_timestamp < $scope.leastRecentCacheTimestamp) { + $scope.leastRecentCacheTimestamp = sub_dir_result.cache_timestamp; + } + } + $scope.leastRecentCacheTimestamp = dataService.convertDatestringToLocalUTCDate($scope.leastRecentCacheTimestamp); + }else { + $scope.ok(); + $scope.$evalAsync(function() { alert("Could not find disk usage for project " + project_name); }); + } + }); + }; + + var loadAllProjectsDiskUsage = function() { + dataService.getProjectsDiskUsage().then(function(result) { + if(result.found) { + $scope.watchedObjectType = OBJECT_TYPE_PROJECTS; + $scope.diskUsageChartConfig.title.text = result.projectdir.name + ' ' + result.projectdir.disk_usage_readable; + $scope.diskUsageChartSeries[0].name = $scope.diskUsageChartConfig.title.text; + $scope.diskUsageChartSeries[0].data.splice(0, $scope.diskUsageChartSeries[0].data.length); + $scope.leastRecentCacheTimestamp = result.projectdir.cache_timestamp; + + var sub_directory_names = Object.keys(result.sub_directories); + sub_directory_names.sort(function(a, b) { return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0)); }); + for(var sub_dir of sub_directory_names) { + var sub_dir_result = result.sub_directories[sub_dir]; + $scope.diskUsageChartSeries[0].data.push({name:sub_dir_result.name + ' ' + sub_dir_result.disk_usage_readable, + y:sub_dir_result.disk_usage || 0, + project_name: sub_dir_result.name }); + + if(sub_dir_result.cache_timestamp < $scope.leastRecentCacheTimestamp) { + $scope.leastRecentCacheTimestamp = sub_dir_result.cache_timestamp; + } + } + $scope.leastRecentCacheTimestamp = dataService.convertDatestringToLocalUTCDate($scope.leastRecentCacheTimestamp); + }else { + $scope.ok(); + $scope.$evalAsync(function() { alert("Could not find disk usage for all projects"); }); + } + }); + }; + + if(task) { + if(task.constructor === Array) { + loadTasksDiskUsage(task); + } else { + loadTaskDiskUsage(task.otdb_id); + } + } else { + loadAllProjectsDiskUsage(); + } + } + }); + }; +}]); + diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js index e524f91b4e2545a417f0d7aa987d7a6be6a54688..d799e1bee2f7b450a11f29840d566d9cbaa978b0 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js @@ -2,6 +2,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, $q){ var self = this; + self.projectMode = false; self.tasks = []; self.resources = []; self.resourceGroups = []; @@ -29,19 +30,83 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.taskTimes = {}; self.resourceClaimTimes = {}; + self.config = {}; + self.selected_resource_id; self.selected_resourceGroup_id; - self.selected_task_id; + self.selected_task_ids = []; self.selected_project_id; self.selected_resourceClaim_id; + self.selected_project = { name: 'Please select project', value: undefined }; + self.initialLoadComplete = false; self.taskChangeCntr = 0; + self.filteredTaskChangeCntr = 0; self.claimChangeCntr = 0; + self.resourceUsagesChangeCntr = 0; self.loadedHours = {}; self.viewTimeSpan = {from: new Date(), to: new Date() }; + self.autoFollowNow = true; + + + self.humanreadablesize = function(num) { + var units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']; + for(unit of units) { + if(Math.abs(num) < 1000.0) { + return num.toPrecision(4).toString() + unit; + } + num /= 1000.0; + } + return num.toPrecision(5).toString() + 'Y'; + } + + self.isTaskIdSelected = function(task_id) { + return self.selected_task_ids.includes(task_id); + } + + self.toggleTaskSelection = function(task_id) { + if(self.isTaskIdSelected(task_id)) { + self.removeSelectedTaskId(task_id); + } else { + self.addSelectedTaskId(task_id); + } + } + + self.addSelectedTaskId = function(task_id) { + if(self.selected_task_ids.indexOf(task_id) == -1) { + self.selected_task_ids.push(task_id); + } + } + + self.removeSelectedTaskId = function(task_id) { + var idx = self.selected_task_ids.indexOf(task_id); + if(idx != -1) { + self.selected_task_ids.splice(idx, 1); + } + } + + self.setSelectedTaskId = function(task_id) { + self.selected_task_ids.splice(0, self.selected_task_ids.length); + self.selected_task_ids.push(task_id); + } + + self.setSelectedTaskIds = function(task_ids) { + self.selected_task_ids.splice(0, self.selected_task_ids.length); + for(var task_id of task_ids) { + self.selected_task_ids.push(task_id); + } + } + + self.selectTasksInSameGroup = function(task) { + self.selected_task_ids.splice(0, self.selected_task_ids.length); + var groupTasks = self.filteredTasks.filter(function(t) { return t.mom_object_group_id == task.mom_object_group_id; }); + for(var t of groupTasks) { + self.selected_task_ids.push(t.id); + } + } self.floorDate = function(date, hourMod=1, minMod=1) { var min = date.getMinutes(); @@ -72,7 +137,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, 'finished': '#00ff00', 'aborted': '#cc0000', 'error': '#990033', - 'obsolete': '#555555'}; + 'obsolete': '#555555', + 'opened': '#d9e5f2', + 'suspended': '#996666'}; @@ -89,27 +156,36 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, //It's a stupid solution, but it works. //-- IMPORTANT REMARKS ABOUT UTC/LOCAL DATE -- - convertDatestringToLocalUTCDate = function(dateString) { + self.convertDatestringToLocalUTCDate = function(dateString) { //first convert the dateString to proper Date var date = new Date(dateString) //then do our trick to offset the timestamp with the utcOffset, see explanation above. return new Date(date.getTime() - self.utcOffset) }; - convertLocalUTCDateToISOString = function(local_utc_date) { - //reverse trick to offset the timestamp with the utcOffset, see explanation above. - var real_utc = new Date(local_utc_date.getTime() + self.utcOffset) - return real_utc.toISOString(); + self.convertLocalUTCDateToISOString = function(local_utc_date) { + if(local_utc_date) { + //reverse trick to offset the timestamp with the utcOffset, see explanation above. + var real_utc = new Date(local_utc_date.getTime() + self.utcOffset) + return real_utc.toISOString(); + } + return undefined; }; //local client time offset to utc in milliseconds self.utcOffset = moment().utcOffset()*60000; + self.convertNullToUndefined = function(in_value) { + return in_value === null ? undefined : in_value; + }; + self.toIdBasedDict = function(list) { var dict = {} - for(var i = list.length-1; i >=0; i--) { - var item = list[i]; - dict[item.id] = item; + if(list) { + for(var i = list.length-1; i >=0; i--) { + var item = list[i]; + dict[item.id] = item; + } } return dict; }; @@ -120,7 +196,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, changedObj.hasOwnProperty(prop) && existingObj[prop] != changedObj[prop]) { if(existingObj[prop] instanceof Date && typeof changedObj[prop] === "string") { - existingObj[prop] = convertDatestringToLocalUTCDate(changedObj[prop]); + existingObj[prop] = self.convertDatestringToLocalUTCDate(changedObj[prop]); } else { existingObj[prop] = changedObj[prop]; } @@ -139,7 +215,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, timestamp += 3600000; } else { - var chuckUpperLimit = Math.min(upperTS, timestamp + 24*3600000); + var chunkFactor = self.projectMode ? 7 : 1; + var chuckUpperLimit = Math.min(upperTS, timestamp + chunkFactor*24*3600000); for (var chunkTimestamp = timestamp; chunkTimestamp < chuckUpperLimit; chunkTimestamp += 3600000) { if(self.loadedHours.hasOwnProperty(chunkTimestamp)) break; @@ -166,18 +243,22 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, var visibleTasks = []; for(var i = 0; i < numTasks; i++) { var task = self.tasks[i]; - if(task.endtime >= from && task.starttime <= until) + if(task.endtime >= from && task.starttime <= until) { visibleTasks.push(task); + } + else { + if(self.isTaskIdSelected(task.id)) { + self.removeSelectedTaskId(task.id); + } + } } self.tasks = visibleTasks; - self.taskDict = self.toIdBasedDict(self.tasks); - self.filteredTasks = self.tasks; - self.filteredTaskDict = self.taskDict; + self.taskDict = self.toIdBasedDict(visibleTasks); + self.taskChangeCntr++; self.computeMinMaxTaskTimes(); - var numClaims = self.resourceClaims.length; var visibleClaims = []; for(var i = 0; i < numClaims; i++) { @@ -204,11 +285,20 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getTasks = function(from, until) { var defer = $q.defer(); var url = '/rest/tasks'; + if(self.projectMode) { + if(self.selected_project_id === undefined){ + defer.resolve([]); + return defer; + } + + url = '/rest/projects/' + self.selected_project_id + '/tasks'; + } + if(from) { - url += '/' + convertLocalUTCDateToISOString(from); + url += '/' + self.convertLocalUTCDateToISOString(from); if(until) { - url += '/' + convertLocalUTCDateToISOString(until); + url += '/' + self.convertLocalUTCDateToISOString(until); } } @@ -216,9 +306,11 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, //convert datetime strings to Date objects for(var i in result.tasks) { var task = result.tasks[i]; - task.starttime = convertDatestringToLocalUTCDate(task.starttime); - task.endtime = convertDatestringToLocalUTCDate(task.endtime); - + task.starttime = self.convertDatestringToLocalUTCDate(task.starttime); + task.endtime = self.convertDatestringToLocalUTCDate(task.endtime); + task.ingest_status = self.convertNullToUndefined(task.ingest_status); + task.disk_usage = self.convertNullToUndefined(task.disk_usage); + task.disk_usage_readable = self.convertNullToUndefined(task.disk_usage_readable); } var initialTaskLoad = self.tasks.length == 0; @@ -228,40 +320,42 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, for(var i = newTaskIds.length-1; i >= 0; i--) { var task_id = newTaskIds[i]; + if(!self.taskDict.hasOwnProperty(task_id)) { var task = newTaskDict[task_id]; - self.tasks.push(task); - self.taskDict[task_id] = task; + if(!self.projectMode || self.selected_project_id == task.project_mom_id) { + self.tasks.push(task); + self.taskDict[task_id] = task; + } } } - self.filteredTasks = self.tasks; - self.filteredTaskDict = self.taskDict; self.taskChangeCntr++; - self.computeMinMaxTaskTimes(); if(initialTaskLoad && self.tasks.length > 0) { setTimeout(function() { + self.selected_task_ids.splice(0,self.selected_task_ids.length); + //try to select current task var currentTasks = self.tasks.filter(function(t) { return t.starttime <= self.lofarTime && t.endtime >= self.lofarTime; }); if(currentTasks.length > 0) { - self.selected_task_id = currentTasks[0].id; + self.selected_task_ids.push(currentTasks[0].id); } else { //try to select next task var nextTasks = self.tasks.filter(function(t) { return t.starttime >= self.lofarTime; }).sort(); if(nextTasks.length > 0) { - self.selected_task_id = nextTasks[0].id; + self.selected_task_ids.push(nextTasks[0].id); } else { //try to select most recent task var prevTasks = self.tasks.filter(function(t) { return t.endtime <= self.lofarTime; }).sort(); if(prevTasks.length > 0) { - self.selected_task_id = prevTasks[prevTasks.length-1].id; + self.selected_task_ids.push(prevTasks[prevTasks.length-1].id); } else { - self.selected_task_id = self.tasks[0].id; + self.selected_task_ids.push(self.tasks[0].id); } } } @@ -275,14 +369,179 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, }; self.putTask = function(task) { - task.starttime = convertLocalUTCDateToISOString(task.starttime); - task.endtime = convertLocalUTCDateToISOString(task.endtime); + task.starttime = self.convertLocalUTCDateToISOString(task.starttime); + task.endtime = self.convertLocalUTCDateToISOString(task.endtime); $http.put('/rest/tasks/' + task.id, task).error(function(result) { console.log("Error. Could not update task. " + result); //TODO: revert to old state }) }; + var _getTaskBy = function(id_name, id, force_reload) { + var defer = $q.defer(); + + if(typeof(id) === 'string') { + id = parseInt(id); + } + + var foundTask = id_name == 'id' ? self.taskDict[id] : self.tasks.find(function(t) { return t[id_name] == id; }); + + if(foundTask && !force_reload) { + defer.resolve(foundTask); + } else { + var url; + switch(id_name) { + case 'id': url = '/rest/tasks/' + id; break; + case 'otdb_id': url = '/rest/tasks/otdb/' + id; break; + case 'mom_id': url = '/rest/tasks/mom/' + id; break; + } + + if(url) { + $http.get(url).success(function(result) { + var task = result.task; + if(task) { + task.starttime = self.convertDatestringToLocalUTCDate(task.starttime); + task.endtime = self.convertDatestringToLocalUTCDate(task.endtime); + task.ingest_status = self.convertNullToUndefined(task.ingest_status); + task.disk_usage = self.convertNullToUndefined(task.disk_usage); + task.disk_usage_readable = self.convertNullToUndefined(task.disk_usage_readable); + + if(!self.taskDict.hasOwnProperty(task.id)) { + self.tasks.push(task); + self.taskDict[task.id] = task; + self.taskChangeCntr++; + } + } + defer.resolve(task); + }).error(function(result) { + defer.resolve(undefined); + }) + } else { + defer.resolve(undefined); + } + } + + return defer.promise; + }; + + self.getTask= function(id, force_reload) { + return _getTaskBy('id', id, force_reload); + }; + + self.getTaskByOTDBId = function(otdb_id, force_reload) { + return _getTaskBy('otdb_id', otdb_id, force_reload); + }; + + self.getTaskByMoMId = function(mom_id, force_reload) { + return _getTaskBy('mom_id', mom_id, force_reload); + }; + + self.getTasksByMoMGroupId = function(mom_object_group_id) { + var defer = $q.defer(); + var url = '/rest/tasks/mom/group/' + mom_object_group_id; + + $http.get(url).success(function(result) { + //convert datetime strings to Date objects + for(var i in result.tasks) { + var task = result.tasks[i]; + task.starttime = self.convertDatestringToLocalUTCDate(task.starttime); + task.endtime = self.convertDatestringToLocalUTCDate(task.endtime); + task.ingest_status = self.convertNullToUndefined(task.ingest_status); + task.disk_usage = self.convertNullToUndefined(task.disk_usage); + task.disk_usage_readable = self.convertNullToUndefined(task.disk_usage_readable); + } + + var newTaskDict = self.toIdBasedDict(result.tasks); + var newTaskIds = Object.keys(newTaskDict); + + for(var i = newTaskIds.length-1; i >= 0; i--) { + var task_id = newTaskIds[i]; + if(!self.taskDict.hasOwnProperty(task_id)) { + var task = newTaskDict[task_id]; + self.tasks.push(task); + self.taskDict[task_id] = task; + } + } + + self.taskChangeCntr++; + self.computeMinMaxTaskTimes(); + + defer.resolve(result.tasks); + }).error(function(result) { + defer.resolve(undefined); + }); + + return defer.promise; + }; + + self.getTasksByMoMParentGroupId = function(mom_object_parent_group_id) { + var defer = $q.defer(); + var url = '/rest/tasks/mom/parentgroup/' + mom_object_parent_group_id; + + $http.get(url).success(function(result) { + //convert datetime strings to Date objects + for(var i in result.tasks) { + var task = result.tasks[i]; + task.starttime = self.convertDatestringToLocalUTCDate(task.starttime); + task.endtime = self.convertDatestringToLocalUTCDate(task.endtime); + task.ingest_status = self.convertNullToUndefined(task.ingest_status); + task.disk_usage = self.convertNullToUndefined(task.disk_usage); + task.disk_usage_readable = self.convertNullToUndefined(task.disk_usage_readable); + } + + var newTaskDict = self.toIdBasedDict(result.tasks); + var newTaskIds = Object.keys(newTaskDict); + + for(var i = newTaskIds.length-1; i >= 0; i--) { + var task_id = newTaskIds[i]; + if(!self.taskDict.hasOwnProperty(task_id)) { + var task = newTaskDict[task_id]; + self.tasks.push(task); + self.taskDict[task_id] = task; + } + } + + self.taskChangeCntr++; + self.computeMinMaxTaskTimes(); + + defer.resolve(result.tasks); + }).error(function(result) { + defer.resolve(undefined); + }); + + return defer.promise; + }; + + self.copyTask = function(task) { + $http.put('/rest/tasks/' + task.id + '/copy').error(function(result) { + console.log("Error. Could not copy task. " + result); + alert("Error: Could not copy task with mom id " + task.mom_id); + }) + }; + + self.getTaskDiskUsageByOTDBId = function(otdb_id) { + var defer = $q.defer(); + $http.get('/rest/tasks/otdb/' + otdb_id + '/diskusage').success(function(result) { + defer.resolve(result); + }).error(function(result) { + defer.resolve({found:false}); + }); + + return defer.promise; + }; + + self.getTaskDiskUsage = function(task) { + var defer = $q.defer(); + $http.get('/rest/tasks/otdb/' + task.otdb_id + '/diskusage').success(function(result) { + result.task = task; + defer.resolve(result); + }).error(function(result) { + defer.resolve({found:false}); + }); + + return defer.promise; + }; + self.computeMinMaxTaskTimes = function() { var starttimes = self.filteredTasks.map(function(t) { return t.starttime;}); var endtimes = self.filteredTasks.map(function(t) { return t.endtime;}); @@ -327,12 +586,14 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, for(var status in resource_usages) { var usages = resource_usages[status]; for(var usage of usages) { - usage.timestamp = convertDatestringToLocalUTCDate(usage.timestamp); + usage.timestamp = self.convertDatestringToLocalUTCDate(usage.timestamp); } } self.resourceUsagesDict[result.resourceusages[i].resource_id] = result.resourceusages[i]; } + self.resourceUsagesChangeCntr++; + defer.resolve(); }); @@ -341,12 +602,16 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getResourceClaims = function(from, until) { var defer = $q.defer(); + if(self.projectMode) { + defer.resolve([]); + return defer; + } var url = '/rest/resourceclaims'; if(from) { - url += '/' + convertLocalUTCDateToISOString(from); + url += '/' + self.convertLocalUTCDateToISOString(from); if(until) { - url += '/' + convertLocalUTCDateToISOString(until); + url += '/' + self.convertLocalUTCDateToISOString(until); } } @@ -354,8 +619,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, //convert datetime strings to Date objects for(var i in result.resourceclaims) { var resourceclaim = result.resourceclaims[i]; - resourceclaim.starttime = convertDatestringToLocalUTCDate(resourceclaim.starttime); - resourceclaim.endtime = convertDatestringToLocalUTCDate(resourceclaim.endtime); + resourceclaim.starttime = self.convertDatestringToLocalUTCDate(resourceclaim.starttime); + resourceclaim.endtime = self.convertDatestringToLocalUTCDate(resourceclaim.endtime); } var newClaimDict = self.toIdBasedDict(result.resourceclaims); @@ -363,8 +628,9 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, for(var i = newClaimIds.length-1; i >= 0; i--) { var claim_id = newClaimIds[i]; + var claim = newClaimDict[claim_id]; + if(!self.resourceClaimDict.hasOwnProperty(claim_id)) { - var claim = newClaimDict[claim_id]; self.resourceClaims.push(claim); self.resourceClaimDict[claim_id] = claim; } @@ -446,7 +712,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getMoMProjects = function() { var defer = $q.defer(); - $http.get('/rest/momprojects').success(function(result) { + $http.get('/rest/projects').success(function(result) { //convert datetime strings to Date objects var dict = {}; var list = []; @@ -478,6 +744,50 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, }); }; + self.getProjectTasksTimeWindow = function(project_mom_id) { + var defer = $q.defer(); + $http.get('/rest/projects/' + project_mom_id + '/taskstimewindow').success(function(result) { + defer.resolve(result); + }).error(function(result) { + defer.resolve(undefined); + }); + + return defer.promise; + }; + + self.getProjectDiskUsage = function(project_name) { + var defer = $q.defer(); + $http.get('/rest/projects/' + project_name + '/diskusage').success(function(result) { + defer.resolve(result); + }).error(function(result) { + defer.resolve({found:false}); + }); + + return defer.promise; + }; + + self.getProjectsDiskUsage = function() { + var defer = $q.defer(); + $http.get('/rest/projects/diskusage').success(function(result) { + defer.resolve(result); + }).error(function(result) { + defer.resolve({found:false}); + }); + + return defer.promise; + }; + + self.getConfig = function() { + var defer = $q.defer(); + $http.get('/rest/config').success(function(result) { + self.config = result.config; + defer.resolve(); + }); + + return defer.promise; + }; + + //start with local client time //lofarTime will be synced with server, //because local machine might have incorrect clock @@ -486,7 +796,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self._syncLofarTimeWithServer = function() { $http.get('/rest/lofarTime', {timeout:1000}).success(function(result) { - self.lofarTime = convertDatestringToLocalUTCDate(result.lofarTime); + self.lofarTime = self.convertDatestringToLocalUTCDate(result.lofarTime); //check if local to utc offset has changed self.utcOffset = moment().utcOffset()*60000; @@ -504,21 +814,17 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.lastUpdateChangeNumber = result.mostRecentChangeNumber; } - var nrOfItemsToLoad = 6; - var nrOfItemsLoaded = 0; - var checkInitialLoadCompleteness = function() { - nrOfItemsLoaded += 1; - if(nrOfItemsLoaded >= nrOfItemsToLoad) { - self.initialLoadComplete = true; - } - }; + var load_promisses = [self.getConfig(), + self.getMoMProjects(), + self.getTaskTypes(), + self.getTaskStatusTypes(), + self.getResourceGroups(), + self.getResources(), + self.getResourceGroupMemberships()]; - self.getMoMProjects().then(checkInitialLoadCompleteness); - self.getTaskTypes().then(checkInitialLoadCompleteness); - self.getTaskStatusTypes().then(checkInitialLoadCompleteness); - self.getResourceGroups().then(checkInitialLoadCompleteness); - self.getResources().then(checkInitialLoadCompleteness); - self.getResourceGroupMemberships().then(checkInitialLoadCompleteness); + $q.all(load_promisses).then(function() { + self.initialLoadComplete = true; + }); self.getTasksAndClaimsForViewSpan(); @@ -553,9 +859,12 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } } else if(change.changeType == 'insert') { var task = self.taskDict[changedTask.id]; - if(!task) { - changedTask.starttime = new Date(changedTask.starttime); - changedTask.endtime = new Date(changedTask.endtime); + if(!task && (!self.projectMode || self.selected_project_id == task.project_mom_id)) { + changedTask.starttime = self.convertDatestringToLocalUTCDate(changedTask.starttime); + changedTask.endtime = self.convertDatestringToLocalUTCDate(changedTask.endtime); + changedTask.ingest_status = self.convertNullToUndefined(changedTask.ingest_status); + changedTask.disk_usage = self.convertNullToUndefined(changedTask.disk_usage); + changedTask.disk_usage_readable = self.convertNullToUndefined(changedTask.disk_usage_readable); self.tasks.push(changedTask); self.taskDict[changedTask.id] = changedTask; } @@ -570,8 +879,12 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } self.taskChangeCntr++; + self.computeMinMaxTaskTimes(); } else if(change.objectType == 'resourceClaim') { + if(self.projectMode) + continue; //skip claims in projectMode + anyResourceClaims = true; var changedClaim = change.value; if(change.changeType == 'update') { @@ -582,8 +895,8 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } else if(change.changeType == 'insert') { var claim = self.resourceClaimDict[changedClaim.id]; if(!claim) { - changedClaim.starttime = new Date(changedClaim.starttime); - changedClaim.endtime = new Date(changedClaim.endtime); + changedClaim.starttime = self.convertDatestringToLocalUTCDate(changedClaim.starttime); + changedClaim.endtime = self.convertDatestringToLocalUTCDate(changedClaim.endtime); self.resourceClaims.push(changedClaim); self.resourceClaimDict[changedClaim.id] = changedClaim; } @@ -596,7 +909,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, } } } - + self.claimChangeCntr++; self.computeMinMaxResourceClaimTimes(); } else if(change.objectType == 'resourceCapacity') { @@ -641,10 +954,11 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, var dataControllerMod = angular.module('DataControllerMod', ['ngResource']); dataControllerMod.controller('DataController', - ['$scope', 'dataService', - function($scope, dataService) { + ['$scope', '$q', 'dataService', + function($scope, $q, dataService) { var self = this; $scope.dataService = dataService; + dataService.dataCtrl = this; $scope.dateOptions = { formatYear: 'yyyy', @@ -656,87 +970,265 @@ dataControllerMod.controller('DataController', $scope.openViewFromDatePopup = function() { $scope.viewFromDatePopupOpened = true; }; $scope.openViewToDatePopup = function() { $scope.viewToDatePopupOpened = true; }; - $scope.jumpTimespanWidths = [{value:30, name:'30 Minutes'}, {value:60, name:'1 Hour'}, {value:3*60, name:'3 Hours'}, {value:6*60, name:'6 Hours'}, {value:12*60, name:'12 Hours'}, {value:24*60, name:'1 Day'}, {value:2*24*60, name:'2 Days'}, {value:3*24*60, name:'3 Days'}, {value:5*24*60, name:'5 Days'}, {value:7*24*60, name:'1 Week'}, {value:14*24*60, name:'2 Weeks'}, {value:28*24*60, name:'4 Weeks'}]; - $scope.jumpTimespanWidth = $scope.jumpTimespanWidths[7]; + $scope.zoomTimespans = [{value:30, name:'30 Minutes'}, {value:60, name:'1 Hour'}, {value:3*60, name:'3 Hours'}, {value:6*60, name:'6 Hours'}, {value:12*60, name:'12 Hours'}, {value:24*60, name:'1 Day'}, {value:2*24*60, name:'2 Days'}, {value:3*24*60, name:'3 Days'}, {value:5*24*60, name:'5 Days'}, {value:7*24*60, name:'1 Week'}, {value:14*24*60, name:'2 Weeks'}, {value:28*24*60, name:'4 Weeks'}, {value:1, name:'Custom (1 min)'}]; + $scope.zoomTimespan = $scope.zoomTimespans[5]; $scope.jumpToNow = function() { var floorLofarTime = dataService.floorDate(dataService.lofarTime, 1, 5); dataService.viewTimeSpan = { - from: dataService.floorDate(new Date(floorLofarTime.getTime() - 0.33*$scope.jumpTimespanWidth.value*60*1000), 1, 5), - to: dataService.ceilDate(new Date(floorLofarTime.getTime() + 0.67*$scope.jumpTimespanWidth.value*60*1000), 1, 5) + from: dataService.floorDate(new Date(floorLofarTime.getTime() - 0.25*$scope.zoomTimespan.value*60*1000), 1, 5), + to: dataService.floorDate(new Date(floorLofarTime.getTime() + 0.75*$scope.zoomTimespan.value*60*1000), 1, 5) }; + }; + + $scope.jumpToNow(); + + $scope.loadTasksSelectAndJumpIntoView = function(task_ids) { + var list_of_promises = task_ids.map(function(t_id) { return $scope.dataService.getTask(t_id); }); + var defer = $q.defer(); + $q.all(list_of_promises).then(function(in_tasks) { + var loaded_tasks = in_tasks.filter(function(t) { return t != undefined; }); + var loaded_tasks_ids = loaded_tasks.map(function(t) { return t.id; }); + $scope.dataService.setSelectedTaskIds(loaded_tasks_ids); + $scope.jumpToSelectedTasks(); + defer.resolve(loaded_tasks); + }); + return defer.promise; + }; + + $scope.loadTaskByOTDBIdSelectAndJumpIntoView = function(otdb_id) { + var defer = $q.defer(); + $scope.dataService.getTaskByOTDBId(otdb_id).then(function(task) { + if(task) { + $scope.dataService.setSelectedTaskId(task.id); + $scope.jumpToSelectedTasks(); + defer.resolve(task); + } else { + defer.resolve(undefined); + } + }); + return defer.promise; + }; + + $scope.loadTaskByMoMIdSelectAndJumpIntoView = function(mom_id) { + var defer = $q.defer(); + $scope.dataService.getTaskByMoMId(mom_id).then(function(task) { + if(task) { + $scope.dataService.setSelectedTaskId(task.id); + $scope.jumpToSelectedTasks(); + defer.resolve(task); + } else { + defer.resolve(undefined); + } + }); + return defer.promise; + }; + + $scope.loadTasksByMoMGroupIdSelectAndJumpIntoView = function(mom_group_id) { + var defer = $q.defer(); + $scope.dataService.getTasksByMoMGroupId(mom_group_id).then(function(tasks) { + if(tasks) { + var task_ids = tasks.map(function(t) { return t.id; }); + + $scope.dataService.setSelectedTaskIds(task_ids); + $scope.jumpToSelectedTasks(); + defer.resolve(tasks); + } else { + defer.resolve(undefined); + } + }); + return defer.promise; + }; + + $scope.loadTasksByMoMParentGroupIdSelectAndJumpIntoView = function(mom_parent_group_id) { + var defer = $q.defer(); + $scope.dataService.getTasksByMoMParentGroupId(mom_parent_group_id).then(function(tasks) { + if(tasks) { + var task_ids = tasks.map(function(t) { return t.id; }); + + $scope.dataService.setSelectedTaskIds(task_ids); - //automatically select current task + if(tasks.length > 1) { + $scope.dataService.selected_project_id = tasks[0].project_mom_id; + } + + $scope.jumpToSelectedTasks(); + defer.resolve(tasks); + } else { + defer.resolve(undefined); + } + }); + return defer.promise; + }; + + $scope.selectCurrentTask = function() { var currentTasks = dataService.tasks.filter(function(t) { return t.starttime <= dataService.viewTimeSpan.to && t.endime >= dataService.viewTimeSpan.from; }); if(currentTasks.lenght > 0) { - dataService.selected_task_id = currentTasks[0].id; + dataService.setSelectedTaskId(currentTasks[0].id); } }; - //initialize are now - $scope.jumpToNow(); - $scope.jumpToSelectedTasks = function() { - if(dataService.selected_task_id == undefined) + if(dataService.selected_task_ids == undefined) return; - var task = dataService.taskDict[dataService.selected_task_id]; + var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); - if(task == undefined) + if(tasks.length == 0) return; - var taskDurationInmsec = task.endtime.getTime() - task.starttime.getTime(); - var taskDurationInMinutes = taskDurationInmsec/60000; - var viewSpanInMinutes = taskDurationInMinutes; + var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; }))); + var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; }))); - var fittingSpans = $scope.jumpTimespanWidths.filter(function(w) { return w.value >= taskDurationInMinutes; }); - if(fittingSpans.length > 0) { - $scope.jumpTimespanWidth = fittingSpans[0]; - viewSpanInMinutes = $scope.jumpTimespanWidth.value; + if(maxEndtime <= minStarttime) { + //swap + var tmp = new Date(maxEndtime.getTime()); + maxEndtime = new Date(minStarttime.getTime()); + minStarttime = new Date(tmp.getTime()); } - var focusTime = new Date(task.starttime.getTime() + 0.5*taskDurationInmsec); + dataService.viewTimeSpan = { + from: dataService.floorDate(minStarttime, 1, 5), + to: dataService.ceilDate(maxEndtime, 1, 5) + }; + dataService.autoFollowNow = false; + }; + $scope.scrollBack = function() { + dataService.autoFollowNow = false; + var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime(); dataService.viewTimeSpan = { - from: dataService.floorDate(new Date(focusTime.getTime() - 0.33*viewSpanInMinutes*60*1000), 1, 5), - to: dataService.ceilDate(new Date(focusTime.getTime() + 0.67*viewSpanInMinutes*60*1000), 1, 5) + from: dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() - 0.25*viewTimeSpanInmsec), 1, 5), + to: dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() - 0.25*viewTimeSpanInmsec), 1, 5) }; }; - $scope.onJumpTimespanWidthChanged = function(span) { - var focusTime = dataService.floorDate(dataService.lofarTime, 1, 5); + $scope.scrollForward = function() { + dataService.autoFollowNow = false; + var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime(); + dataService.viewTimeSpan = { + from: dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() + 0.25*viewTimeSpanInmsec), 1, 5), + to: dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() + 0.25*viewTimeSpanInmsec), 1, 5) + }; + }; - if(dataService.selected_task_id != undefined) { - var task = dataService.taskDict[dataService.selected_task_id]; + $scope.onZoomTimespanChanged = function() { + var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime(); + var focusTime = new Date(dataService.viewTimeSpan.from + 0.5*viewTimeSpanInmsec); - if(task) { - focusTime = dataService.floorDate(task.starttime, 1, 5); + if(dataService.autoFollowNow) { + focusTime = dataService.floorDate(dataService.lofarTime, 1, 5); + } else { + var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); + + if(tasks.lenght > 0) { + var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; }))); + var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; }))); + + focusTime = dataService.floorDate(new Date(0.5*(minStarttime.getTime() + maxEndtime.getTime())), 1, 5); } } dataService.viewTimeSpan = { - from: dataService.floorDate(new Date(focusTime.getTime() - 0.33*$scope.jumpTimespanWidth.value*60*1000)), - to: dataService.ceilDate(new Date(focusTime.getTime() + 0.67*$scope.jumpTimespanWidth.value*60*1000)) + from: dataService.floorDate(new Date(focusTime.getTime() - 0.25*$scope.zoomTimespan.value*60*1000)), + to: dataService.floorDate(new Date(focusTime.getTime() + 0.75*$scope.zoomTimespan.value*60*1000)) }; }; - $scope.$watch('dataService.viewTimeSpan.from', function() { - if(dataService.viewTimeSpan.from >= dataService.viewTimeSpan.to) { - dataService.viewTimeSpan.to = dataService.ceilDate(new Date(dataService.viewTimeSpan.from.getTime() + 60*60*1000), 1, 5); + $scope.selectZoomTimespan = function() { + var viewTimeSpanInmsec = dataService.viewTimeSpan.to.getTime() - dataService.viewTimeSpan.from.getTime(); + var viewTimeSpanInMinutes = Math.round(viewTimeSpanInmsec/60000); + + var foundZoomTimespan = $scope.zoomTimespans.find(function(zts) { return zts.value == viewTimeSpanInMinutes; }); + + if(foundZoomTimespan) { + $scope.zoomTimespan = foundZoomTimespan; + } else { + var customZoomTimespan = $scope.zoomTimespans.find(function(zts) { return zts.name.startsWith('Custom'); }); + customZoomTimespan.value = viewTimeSpanInMinutes; + if(viewTimeSpanInMinutes < 1440) { + customZoomTimespan.name = 'Custom (' + viewTimeSpanInMinutes + ' min)'; + } else { + var viewTimeSpanInDays = Math.floor(viewTimeSpanInMinutes / 1440); + var viewTimeSpanReaminingMinutes = viewTimeSpanInMinutes - viewTimeSpanInDays * 1440; + customZoomTimespan.name = 'Custom (' + viewTimeSpanInDays + ' days ' + viewTimeSpanReaminingMinutes + ' min)'; + } + $scope.zoomTimespan = customZoomTimespan; } - }); + }; - $scope.$watch('dataService.viewTimeSpan.to', function() { - if(dataService.viewTimeSpan.to <= dataService.viewTimeSpan.from) { - dataService.viewTimeSpan.from = dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() - 60*60*1000), 1, 5); + $scope.onViewTimeSpanFromChanged = function() { + if (!isNaN(dataService.viewTimeSpan.from)) { + dataService.autoFollowNow = false; + if(dataService.viewTimeSpan.from >= dataService.viewTimeSpan.to) { + dataService.viewTimeSpan.to = dataService.floorDate(new Date(dataService.viewTimeSpan.from.getTime() + $scope.zoomTimespan.value*60*1000), 1, 5); + } } - }); + }; + + $scope.onViewTimeSpanToChanged = function() { + if (!isNaN(dataService.viewTimeSpan.to)) { + dataService.autoFollowNow = false; + if(dataService.viewTimeSpan.to <= dataService.viewTimeSpan.from) { + dataService.viewTimeSpan.from = dataService.floorDate(new Date(dataService.viewTimeSpan.to.getTime() - $scope.zoomTimespan.value*60*1000), 1, 5); + } + } + }; + + $scope.getFullTimeWindowForSelectedProject = function() { + dataService.getProjectTasksTimeWindow(dataService.selected_project_id).then(function(window) { + if(window.min_starttime && window.max_endtime) { + dataService.viewTimeSpan.from = dataService.convertDatestringToLocalUTCDate(window.min_starttime); + dataService.viewTimeSpan.to = dataService.convertDatestringToLocalUTCDate(window.max_endtime); + } + }); + }; $scope.$watch('dataService.viewTimeSpan', function() { - dataService.clearTasksAndClaimsOutsideViewSpan(); - dataService.getTasksAndClaimsForViewSpan(); + $scope.selectZoomTimespan(); + + $scope.$evalAsync(function() { dataService.clearTasksAndClaimsOutsideViewSpan(); }); + $scope.$evalAsync(function() { dataService.getTasksAndClaimsForViewSpan(); }); }, true); - $scope.$watch('dataService.filteredTasks', dataService.computeMinMaxTaskTimes); + $scope.$watch('dataService.filteredTaskChangeCntr', dataService.computeMinMaxTaskTimes); + + $scope.$watch('dataService.lofarTime', function() { + if(dataService.autoFollowNow && (Math.round(dataService.lofarTime.getTime()/1000))%5==0) { + $scope.jumpToNow(); + } + }); + + $scope.$watch('dataService.autoFollowNow', function() { + if(dataService.autoFollowNow) { + $scope.jumpToNow(); + } + }); + + $scope.$watch('dataService.selected_project_id', function() { + if(dataService.projectMode) { + $scope.$evalAsync(function() { + dataService.autoFollowNow = false; + dataService.viewTimeSpan.from = dataService.lofarTime; + dataService.viewTimeSpan.to = dataService.lofarTime; + dataService.tasks.splice(0, dataService.tasks.length); + dataService.tasksDict = dataService.toIdBasedDict(dataService.tasks); + dataService.taskChangeCntr++; + dataService.getProjectTasksTimeWindow(dataService.selected_project_id).then(function(window) { + if(window.min_starttime && window.max_endtime) { + dataService.viewTimeSpan.from = dataService.convertDatestringToLocalUTCDate(window.min_starttime); + dataService.viewTimeSpan.to = dataService.convertDatestringToLocalUTCDate(window.max_endtime); + dataService.getTasksAndClaimsForViewSpan(); + } + }); + }); + } + }); + + $scope.$watch('dataService.selected_project', function() { + $scope.$evalAsync(function() { + dataService.selected_project_id = dataService.selected_project.value; + }); + }); dataService.initialLoad(); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js index 4ad95c83910933dd21a1f5097587c66e45dbf12a..934afd7b81896ec03c0970f1b86149fa1b569e37 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js @@ -13,7 +13,8 @@ var ganttProjectControllerMod = angular.module('GanttProjectControllerMod', [ 'gantt.groups', 'gantt.dependencies', 'gantt.overlap', - 'gantt.resizeSensor']).config(['$compileProvider', function($compileProvider) { + 'gantt.resizeSensor', + 'gantt.contextmenu']).config(['$compileProvider', function($compileProvider) { $compileProvider.debugInfoEnabled(false); // Remove debug info (angularJS >= 1.3) }]); @@ -24,8 +25,11 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS $scope.dataService = dataService; $scope.ganttData = []; + $scope.enabled = true; self.taskStatusColors = dataService.taskStatusColors; + self.lastUpdateTimestamp = new Date(0); + self.waitingForDelayedUpdate = false; $scope.options = { mode: 'custom', @@ -34,10 +38,10 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS currentDateValue: $scope.dataService.lofarTime, columnMagnet: '1 minutes', timeFramesMagnet: false, - sideMode: 'Tree', + sideMode: 'Table', autoExpand: 'both', taskOutOfRange: 'truncate', - dependencies: true, + dependencies: false, api: function(api) { // API Object is used to control methods and events from angular-gantt. $scope.api = api; @@ -48,34 +52,38 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS } ); - api.directives.on.new($scope, function(directiveName, directiveScope, element) { + api.directives.on.new($scope, function(directiveName, directiveScope, directiveElement) { if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' ) { - element.bind('click', function(event) { + directiveElement.bind('click', function(event) { if(directiveScope.row.model.project) { $scope.dataService.selected_project_id = directiveScope.row.model.project.id; } }); } else if (directiveName === 'ganttTask') { - element.bind('click', function(event) { + directiveElement.bind('click', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + if(event.ctrlKey) { + $scope.dataService.toggleTaskSelection(directiveScope.task.model.raTask.id); + } else { + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); + } } }); - element.bind('dblclick', function(event) { + directiveElement.bind('dblclick', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); $scope.jumpToSelectedTasks(); } }); } }); - api.directives.on.destroy($scope, function(directiveName, directiveScope, element) { + api.directives.on.destroy($scope, function(directiveName, directiveScope, directiveElement) { if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' || directiveName === 'ganttTask') { - element.unbind('click'); + directiveElement.unbind('click'); } if (directiveName === 'ganttTask') { - element.unbind('dblclick'); + directiveElement.unbind('dblclick'); } }); } @@ -96,7 +104,30 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS } }; + function updateGanttDataAsync() { + $scope.$evalAsync(updateGanttData); + }; + + function updateGanttDataAsync() { + var now = new Date(); + var diff = now.getTime() - self.lastUpdateTimestamp.getTime(); + if(diff > 500) { + self.waitingForDelayedUpdate = false; + $scope.$evalAsync(updateGanttData); + } + else { + if (!self.waitingForDelayedUpdate) { + self.waitingForDelayedUpdate = true; + setTimeout(updateGanttDataAsync, diff); + } + } + }; + function updateGanttData() { + if(!$scope.enabled) { + return; + } + if(!dataService.initialLoadComplete) { return; } @@ -104,27 +135,23 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS var projectsDict = $scope.dataService.momProjectsDict; var numProjecs = $scope.dataService.momProjects.length; - var taskDict = $scope.dataService.filteredTaskDict; - var tasks = $scope.dataService.filteredTasks; - var numTasks = tasks.length; - - var tasktypesDict = $scope.dataService.tasktypesDict; - var tasktypes = $scope.dataService.tasktypes; - var numTasktypes = tasktypes.length; - - if(numProjecs == 0 || numTasktypes == 0){ + if(numProjecs == 0) { $scope.ganttData = []; return; } - var editableTaskStatusIds = $scope.dataService.editableTaskStatusIds; + var taskDict = $scope.dataService.filteredTaskDict; + var tasks = $scope.dataService.filteredTasks; + var numTasks = tasks.length; - var ganntRowsDict = {}; + var ganntRows = []; - if(numProjecs > 0 && numTasks > 0 && numTasktypes > 0) { - $scope.options.fromDate = $scope.dataService.viewTimeSpan.from; - $scope.options.toDate = $scope.dataService.viewTimeSpan.to; - var fullTimespanInMinutes = ($scope.options.toDate - $scope.options.fromDate) / (60 * 1000); + if(numProjecs > 0 && numTasks > 0) { + var lowerViewBound = $scope.dataService.viewTimeSpan.from; + var upperViewBound = $scope.dataService.viewTimeSpan.to; + $scope.options.fromDate = lowerViewBound; + $scope.options.toDate = upperViewBound; + var fullTimespanInMinutes = (upperViewBound - lowerViewBound) / (60 * 1000); if(fullTimespanInMinutes > 28*24*60) { $scope.options.viewScale = '7 days'; @@ -142,98 +169,172 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS $scope.options.viewScale = '15 minutes'; } - for(var i = 0; i < numTasks; i++) { - var task = tasks[i]; - - var projectRowId = 'project_' + task.project_mom_id; - var ganntProjectRow = ganntRowsDict[projectRowId]; + //only enable dependencies (arrows between tasks) in detailed view + $scope.options.dependencies = (fullTimespanInMinutes <= 6*60 && numTasks <= 100) || numTasks < 20; - if(!ganntProjectRow) { - var project = projectsDict[task.project_mom_id]; + //start with aggregating all tasks per type, + //and plot these in the upper rows, + //so we can see the observartion and pipeline scheduling usage/efficiency + for(var type of ['observation', 'pipeline']) { + var typeTasks = tasks.filter(function(t) { return t.type == type;}).sort(function(a, b) { return a.starttime.getTime() - b.starttime.getTime(); });; + var numTypeTasks = typeTasks.length; - if(project) { - ganntProjectRow = { - id: projectRowId, - name: project.name, - project: project, + if(numTypeTasks > 0) { + var typeAggregateRow = { + id: type + 's_aggregated', + name: ('All ' + type + 's').toUpperCase(), tasks: [] }; + ganntRows.push(typeAggregateRow); + + var task = typeTasks[0]; + + var rowTask = { + id: typeAggregateRow.id + '_task_' + typeAggregateRow.tasks.length, + from: task.starttime, + color: '#cceecc', + movable: false + }; - ganntRowsDict[projectRowId] = ganntProjectRow; + if(rowTask.from < lowerViewBound) { + rowTask.from = lowerViewBound; } - } - if(ganntProjectRow) { - var typeRowId = 'project_' + task.project_mom_id + '_type_' + task.type_id; - var ganntTypeRow = ganntRowsDict[typeRowId]; + for(var i = 1; i < numTypeTasks; i++) { + var prev_task = task; + task = typeTasks[i]; + + if(task.starttime > prev_task.endtime) { + rowTask.to = prev_task.endtime; + + if(rowTask.to > upperViewBound) { + rowTask.to = upperViewBound; + } - if(!ganntTypeRow) { - var tasktype = tasktypesDict[task.type_id].name; + typeAggregateRow.tasks.push(rowTask); - if(tasktype) { - ganntTypeRow = { - id: typeRowId, - parent: projectRowId, - name: tasktype, - project: project, - tasks: [] + rowTask = { + id: typeAggregateRow.id + '_task_' + typeAggregateRow.tasks.length, + from: task.starttime, + color: '#cceecc', + movable: false }; - ganntRowsDict[typeRowId] = ganntTypeRow; + if(rowTask.from < lowerViewBound) { + rowTask.from = lowerViewBound; + } } } - if(ganntTypeRow) { - var rowTask = { - id: task.id.toString(), - name: task.name, - from: task.starttime, - to: task.endtime, - raTask: task, - color: self.taskStatusColors[task.status], - classes: 'task-status-' + task.status, - movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 - }; + if(!rowTask.to) { + rowTask.to = task.endtime; - if(task.id == dataService.selected_task_id) { - rowTask.classes += ' task-selected-task'; + if(rowTask.to > upperViewBound) { + rowTask.to = upperViewBound; } + } - if(task.predecessor_ids && task.predecessor_ids.length > 0) { - rowTask['dependencies'] = []; - for(var predId of task.predecessor_ids) { - rowTask['dependencies'].push({'from': predId}); - } - } + typeAggregateRow.tasks.push(rowTask); + var aggTaskTotalDuration = 0.0 + typeAggregateRow.tasks.map(function(t) { return t.to - t.from;}).reduce(function(a, b) { return a+b; }); + var usage = aggTaskTotalDuration / (upperViewBound - lowerViewBound); + var usagePerc = parseFloat(Math.round(100.0 * usage * 10.0) / 10.0).toFixed(1); + typeAggregateRow.name += ' (' + usagePerc + '%)'; + } + } + + var editableTaskStatusIds = $scope.dataService.editableTaskStatusIds; + var ganntRowsDict = {}; + + for(var i = 0; i < numTasks; i++) { + var task = tasks[i]; + var project = projectsDict[task.project_mom_id]; + + if(!project) { + continue; + } - ganntTypeRow.tasks.push(rowTask); + var projectTypeRowsId = 'project_' + task.project_mom_id + '_type_' + task.type_id; + var ganntProjectTypeRows = ganntRowsDict[projectTypeRowsId]; + + if(!ganntProjectTypeRows) { + ganntProjectTypeRows = []; + ganntRowsDict[projectTypeRowsId] = ganntProjectTypeRows; + } + + var availableRow = ganntProjectTypeRows.find(function(row) { + var overlappingTasks = row.tasks.filter(function(t) { + return (t.from >= task.starttime && t.from <= task.endtime) || + (t.to >= task.starttime && t.to <= task.endtime) || + (t.from <= task.starttime && t.to >= task.endtime); + }); + return overlappingTasks.length == 0; + }); + + if(!availableRow) + { + availableRow = { + id: projectTypeRowsId + '_' + (ganntProjectTypeRows.length+1), + name: project.name + ' ' + task.type, + project: project, + tasks: [] + }; + + ganntProjectTypeRows.push(availableRow); + ganntRows.push(availableRow); + } + + // Scheduled tasks that are blocked tasks are shown differently and use a tooltip + var css_class = "task-status-"; + if (task.blocked_by_ids.length > 0) { + css_class += "blocked"; + } + else { + css_class += task.status; + } + + var rowTask = { + id: task.id.toString(), + name: task.name, + from: task.starttime, + to: task.endtime, + raTask: task, + + // Leave color property undefined; it is now defined by CSS + //color: self.taskStatusColors[task.status], + + classes: css_class, + movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 + }; + + if(dataService.isTaskIdSelected(task.id)) { + rowTask.classes += ' task-selected-task'; + } + + if($scope.options.dependencies && task.predecessor_ids && task.predecessor_ids.length > 0) { + rowTask['dependencies'] = []; + for(var predId of task.predecessor_ids) { + rowTask['dependencies'].push({'from': predId}); } } + + availableRow.tasks.push(rowTask); } } - var ganntRows = []; - - for (var rowId in ganntRowsDict) - ganntRows.push(ganntRowsDict[rowId]); - - ganntRows.sort(function(a, b) { return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0)); }); $scope.ganttData = ganntRows; + self.lastUpdateTimestamp = new Date(); }; - $scope.$watch('dataService.initialLoadComplete', updateGanttData); - $scope.$watch('dataService.selected_task_id', updateGanttData); - $scope.$watch('dataService.tasks', updateGanttData); - $scope.$watch('dataService.resources', updateGanttData); - $scope.$watch('dataService.resourceClaims', updateGanttData); - $scope.$watch('dataService.resourceGroups', updateGanttData); - $scope.$watch('dataService.resourceGroupMemberships', updateGanttData); - $scope.$watch('dataService.filteredTaskDict', updateGanttData); - $scope.$watch('dataService.momProjectsDict', updateGanttData); - $scope.$watch('dataService.viewTimeSpan', updateGanttData, true); - $scope.$watch('dataService.taskChangeCntr', updateGanttData); + $scope.$watch('dataService.initialLoadComplete', updateGanttDataAsync); + $scope.$watch('dataService.selected_task_ids', updateGanttDataAsync, true); + $scope.$watch('dataService.viewTimeSpan', updateGanttDataAsync, true); + $scope.$watch('dataService.filteredTaskChangeCntr', updateGanttDataAsync); + $scope.$watch('enabled', function() { setTimeout(updateGanttDataAsync, 500); } ); $scope.$watch('dataService.lofarTime', function() { - if($scope.dataService.lofarTime.getSeconds() % 5 == 0) { - $scope.options.currentDateValue= $scope.dataService.lofarTime;}}); + $scope.$evalAsync(function() { + if($scope.dataService.lofarTime.getSeconds() % 10 == 0) { + $scope.options.currentDateValue= $scope.dataService.lofarTime;} + }); + }); } ]); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js index 312e7885528aa5357b1e9374af46483bb10b5cab..9c201da4ac806d1cc6f432bd0702935604889848 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js @@ -11,20 +11,23 @@ var ganttResourceControllerMod = angular.module('GanttResourceControllerMod', [ 'gantt.tree', 'gantt.groups', 'gantt.overlap', - 'gantt.resizeSensor']).config(['$compileProvider', function($compileProvider) { + 'gantt.resizeSensor', + 'gantt.contextmenu']).config(['$compileProvider', function($compileProvider) { $compileProvider.debugInfoEnabled(false); // Remove debug info (angularJS >= 1.3) }]); ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dataService', function($scope, dataService) { var self = this; - self.doInitialCollapse = true; $scope.dataService = dataService; $scope.ganttData = [] + $scope.enabled = true; self.taskStatusColors = dataService.taskStatusColors; self.resourceClaimStatusColors = dataService.resourceClaimStatusColors; + self.lastUpdateTimestamp = new Date(0); + self.waitingForDelayedUpdate = false; $scope.options = { mode: 'custom', @@ -45,9 +48,9 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat api.tasks.on.resizeEnd($scope, moveHandler); }); - api.directives.on.new($scope, function(directiveName, directiveScope, element) { + api.directives.on.new($scope, function(directiveName, directiveScope, directiveElement) { if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' ) { - element.bind('click', function(event) { + directiveElement.bind('click', function(event) { if(directiveScope.row.model.resource) { $scope.dataService.selected_resource_id = directiveScope.row.model.resource.id; } else if(directiveScope.row.model.resourceGroup) { @@ -55,20 +58,59 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat } }); } else if (directiveName === 'ganttTask') { - element.bind('click', function(event) { + directiveElement.bind('click', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + if(event.ctrlKey) { + $scope.dataService.toggleTaskSelection(directiveScope.task.model.raTask.id); + } else { + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); + } } if(directiveScope.task.model.claim) { $scope.dataService.selected_resourceClaim_id = directiveScope.task.model.claim.id; } }); + directiveElement.bind('contextmenu', function(event) { + //search for already existing contextmenu element + if(directiveElement.find('#gantt-resource-context-menu').length) { + //found, remove it, so we can create a fresh one + directiveElement.find('#gantt-resource-context-menu')[0].remove(); + } + + //create contextmenu element + //with list of menu items, + //each with it's own action + var contextmenuElement = angular.element('<div id="gantt-resource-context-menu"></div>'); + ulElement = angular.element('<ul style="z-index:10000; position:fixed; top:initial; left:initial; display:block;" role="menu" class="dropdown-menu"></ul>'); + contextmenuElement.append(ulElement); + liElement = angular.element('<li><a href="#">Copy Task</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + $scope.dataService.copyTask(directiveScope.task.model.raTask); + closeContextMenu(); + }); + + var closeContextMenu = function() { + contextmenuElement.remove(); + angular.element(document).unbind('click', closeContextMenu); + }; + + //click anywhere to remove the contextmenu + angular.element(document).bind('click', closeContextMenu); + + //add contextmenu to clicked element + directiveElement.append(contextmenuElement); + + //prevent bubbling event upwards + return false; + }); } }); - api.directives.on.destroy($scope, function(directiveName, directiveScope, element) { + api.directives.on.destroy($scope, function(directiveName, directiveScope, directiveElement) { if (directiveName === 'ganttRow' || directiveName === 'ganttRowLabel' || directiveName === 'ganttTask') { - element.unbind('click'); + directiveElement.unbind('click'); + directiveElement.unbind('contextmenu'); } }); } @@ -85,7 +127,26 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat $scope.dataService.putTask(updatedTask); }; + function updateGanttDataAsync() { + var now = new Date(); + var diff = now.getTime() - self.lastUpdateTimestamp.getTime(); + if(diff > 500) { + self.waitingForDelayedUpdate = false; + $scope.$evalAsync(updateGanttData); + } + else { + if (!self.waitingForDelayedUpdate) { + self.waitingForDelayedUpdate = true; + setTimeout(updateGanttDataAsync, diff); + } + } + }; + function updateGanttData() { + if(!$scope.enabled) { + return; + } + if(!dataService.initialLoadComplete) { return; } @@ -295,10 +356,9 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 }; - if(claim.id == dataService.selected_resourceClaim_id) { claimTask.classes += ' claim-selected-claim'; - } else if(task.id == dataService.selected_task_id) { + } else if(dataService.isTaskIdSelected(task.id)) { claimTask.classes += ' claim-selected-task'; } @@ -387,7 +447,7 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 }; - if(task.id == dataService.selected_task_id) { + if(dataService.isTaskIdSelected(task.id)) { claimTask.classes += ' claim-selected-task'; } @@ -420,26 +480,20 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat ganttRows.push(ganttRowsDict[rowId]); $scope.ganttData = ganttRows; - - if(self.doInitialCollapse && numResources && numResourceGroups) - { - doInitialCollapse = false; -// setTimeout(function() { $scope.api.tree.collapseAll(); }, 50); - } + self.lastUpdateTimestamp = new Date(); }; - $scope.$watch('dataService.initialLoadComplete', updateGanttData); - $scope.$watch('dataService.selected_task_id', updateGanttData); - $scope.$watch('dataService.tasks', updateGanttData); - $scope.$watch('dataService.resources', updateGanttData); - $scope.$watch('dataService.resourceClaims', updateGanttData); - $scope.$watch('dataService.resourceGroups', updateGanttData); - $scope.$watch('dataService.resourceGroupMemberships', updateGanttData); - $scope.$watch('dataService.filteredTaskDict', updateGanttData); - $scope.$watch('dataService.viewTimeSpan', updateGanttData, true); - $scope.$watch('dataService.claimChangeCntr', updateGanttData); + $scope.$watch('dataService.initialLoadComplete', updateGanttDataAsync); + $scope.$watch('dataService.selected_task_ids', updateGanttDataAsync, true); + $scope.$watch('dataService.viewTimeSpan', updateGanttDataAsync, true); + $scope.$watch('dataService.claimChangeCntr', updateGanttDataAsync); + $scope.$watch('dataService.filteredTaskChangeCntr', updateGanttDataAsync); + $scope.$watch('enabled', function() { setTimeout(updateGanttDataAsync, 500); } ); $scope.$watch('dataService.lofarTime', function() { - if($scope.dataService.lofarTime.getSeconds() % 5 == 0) { - $scope.options.currentDateValue= $scope.dataService.lofarTime;}}); + $scope.$evalAsync(function() { + if($scope.dataService.lofarTime.getSeconds() % 10 == 0) { + $scope.options.currentDateValue= $scope.dataService.lofarTime;} + }); + }); } ]); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js index ae7f5cf8513ce87bae97a21780257fdd699d136e..0c0053601abbe83cddf360e1c9d0c23125f9b5aa 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js @@ -3,24 +3,88 @@ var gridControllerMod = angular.module('GridControllerMod', ['ui.grid', 'ui.grid.edit', 'ui.grid.selection', - 'ui.grid.cellNav', 'ui.grid.resizeColumns', 'ui.grid.autoResize']); -gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGridConstants', function($scope, dataService, uiGridConstants) { +gridControllerMod.controller('GridController', ['$scope', '$window', 'dataService', 'uiGridConstants', function($scope, $window, dataService, uiGridConstants) { + + var self = this; + self.lastUpdateTimestamp = new Date(0); + self.waitingForDelayedUpdate = false; $scope.dataService = dataService; + + $scope.sanitize_url = function(in_url) { + var split_url = in_url.split("://"); + return split_url[0] + "://" + split_url[1].replace(/\/+/g, "/"); + } + + $scope.selectBlockingPredecessors = function(in_blocking_predecessors) { + $scope.$parent.$parent.loadTasksSelectAndJumpIntoView(in_blocking_predecessors); + }; + + $scope.openLtaLocation = function(in_ingest_tasks) { + var ingest_tasks = Array.isArray(in_ingest_tasks) ? in_ingest_tasks : [in_ingest_tasks]; + + //example: http://lofar.target.rug.nl/Lofar?mode=query_result_page_user&product=AveragingPipeline&ObservationId=544965&project=LC6_015 + //map task.sub_type to url product parameter + var project2project2product2tasksDict = {}; + + for(var t of ingest_tasks) { + var lta_product; + switch(t.sub_type) { + case 'averaging_pipeline': lta_product = 'AveragingPipeline'; break; + case 'calibration_pipeline': lta_product = 'CalibrationPipeline'; break; + case 'pulsar_pipeline': lta_product = 'PulsarPipeline'; break; + case 'lofar_imaging_pipeline': lta_product = 'ImagingPipeline'; break; + case 'imaging_pipeline_msss': lta_product = 'ImagingPipeline'; break; + case 'long_baseline_pipeline': lta_product = 'LongBaselinePipeline'; break; + case 'lofar_observation': lta_product = 'Observation'; break; + } + if(lta_product && (t.ingest_status != undefined) ) { + if(!project2project2product2tasksDict.hasOwnProperty(t.project_name)) { + project2project2product2tasksDict[t.project_name] = {}; + } + if(!project2project2product2tasksDict[t.project_name].hasOwnProperty(lta_product)) { + project2project2product2tasksDict[t.project_name][lta_product] = []; + } + project2project2product2tasksDict[t.project_name][lta_product].push(t); + } + } + + var window_cntr = 0; + for(var project in project2project2product2tasksDict) { + for(var product in project2project2product2tasksDict[project]) { + var product_tasks = project2project2product2tasksDict[project][product]; + var otdb_ids = product_tasks.map(function(pt) { return pt.otdb_id; }); + var otdb_ids_string = otdb_ids.join(','); + var url = dataService.config.lta_base_url + '/Lofar?mode=query_result_page_user&product=' + product + '&ObservationId=' + otdb_ids_string + '&project=' + project; + url = $scope.sanitize_url(url); + setTimeout(function(url_arg) { + $window.open(url_arg, '_blank'); + }, window_cntr*250, url); + window_cntr += 1; + } + } + } $scope.columns = [ { field: 'name', enableCellEdit: false, - width: '15%' + cellTooltip: function(row, col) { return row.entity.description; }, + width: '*', + minWidth: '100', }, { field: 'project_name', displayName:'Project', enableCellEdit: false, - cellTemplate:'<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.project_mom2object_id}}">{{row.entity[col.field]}}</a>', - width: '15%', + cellTemplate:'<div style=\'padding-top:5px;\'>' + + '<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.project_mom2object_id}}"' + + ' title="{{row.grid.appScope.dataService.momProjectsDict[row.entity.project_mom_id].description}}"' + + '>{{row.entity[col.field]}}' + + '</a></div>', + width: '*', + minWidth: '80', filter: { type: uiGridConstants.filter.SELECT, selectOptions: [] @@ -28,43 +92,110 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid }, { field: 'starttime', displayName: 'Start', - width: '14%', + width: '120', type: 'date', enableCellEdit: false, enableCellEditOnFocus: false, - cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>', - sort: { direction: uiGridConstants.ASC } + cellTemplate:'<div style=\'text-align:center; padding-top:5px;\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>', + sort: { direction: uiGridConstants.ASC, priority: 3 } }, { field: 'endtime', displayName: 'End', - width: '14%', + width: '120', type: 'date', enableCellEdit: false, enableCellEditOnFocus: false, - cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>' + cellTemplate:'<div style=\'text-align:center; padding-top:5px;\'>{{row.entity[col.field] | date:\'yyyy-MM-dd HH:mm:ss\'}}</div>' }, { field: 'duration', displayName: 'Duration', - width: '8%', + width: '70', + type: 'number', enableFiltering: false, enableCellEdit: false, enableCellEditOnFocus: false, - cellTemplate:'<div style=\'text-align:left\'>{{row.entity[col.field] | secondsToHHmmss}}</div>' + cellTemplate:'<div style=\'text-align:center; padding-top:5px;\'>{{row.entity[col.field] | secondsToHHmmss}}</div>' }, { field: 'status', - enableCellEdit: true, - width: '8%', + enableCellEdit: false, + width: '70', + filter: { + condition: uiGridConstants.filter.EXACT, + type: uiGridConstants.filter.SELECT, + selectOptions: [] + }, + cellClass: function(grid, row, col, rowRenderIndex, colRenderIndex) { + return "grid-status-" + grid.getCellValue(row,col); + } + // Supposedly [1], select-box items can be formatted using: + // headerCellFilter: 'statusFormatter' + // + // [1] http://stackoverflow.com/questions/37286945/ui-grid-setting-template-for-filter-options + }, + { field: 'info', + displayName: 'Info', + enableCellEdit: false, + width: '45', filter: { + condition: function(searchTerm, cellValue, row, column) { + var do_include = false; + switch(searchTerm) { + case 0: do_include = (row.entity.blocked_by_ids.length > 0); break; + case 1: do_include = (row.entity.ingest_status=="ingesting"); break; + case 2: do_include = (row.entity.ingest_status=="ingested"); break; + case 3: do_include = (row.entity.ingest_status=="failed"); break; + default: break; + }; + return do_include; + }, type: uiGridConstants.filter.SELECT, selectOptions: [] }, editableCellTemplate: 'ui-grid/dropdownEditor', - editDropdownOptionsArray: [] + editDropdownOptionsArray: [], + headerTooltip: "Additional status information", + cellTemplate: '<div style="text-align: center" class="ui-grid-cell-contents">' + + '<span ng-if="row.entity.blocked_by_ids.length > 0"><img ng-click="row.grid.appScope.selectBlockingPredecessors(row.entity.blocked_by_ids)" ng-src="static/icons/blocked.png" title="Blocked by {{row.entity.blocked_by_ids.length.toString()}} predecessor(s) - Click to select" /></span>' + + '<span ng-if="row.entity.ingest_status==\'ingesting\'"><img ng-click="row.grid.appScope.openLtaLocation(row.entity)" ng-src="static/icons/ingest_in_progress.png" title="Ingest in progress - Click to open LTA catalog" /></span>' + + '<span ng-if="row.entity.ingest_status==\'ingested\'"><img ng-click="row.grid.appScope.openLtaLocation(row.entity)" ng-src="static/icons/ingest_successful.png" title="Ingest successful - Click to open LTA catalog" /></span>' + + '<span ng-if="row.entity.ingest_status==\'failed\'"><img ng-click="row.grid.appScope.openLtaLocation(row.entity)" ng-src="static/icons/ingest_failed.png" title="Ingest failed - Click to open LTA catalog" /></span>' + + '</div>' }, { field: 'type', enableCellEdit: false, - width: '8%', + width: '80', + filter: { + condition: uiGridConstants.filter.EXACT, + type: uiGridConstants.filter.SELECT, + selectOptions: [] + }, + sort: { direction: uiGridConstants.ASC, priority: 2 } + }, + { field: 'disk_usage', + displayName: 'Size', + type: 'number', + enableCellEdit: false, + cellTemplate:'<div style=\'text-align:right; padding-top: 5px;\'>{{row.entity.disk_usage_readable}}</div>', + width: '80', filter: { + type: uiGridConstants.filter.SELECT, + condition: uiGridConstants.filter.GREATER_THAN, + selectOptions: [{ value:1e9, label: '> 1G'}, { value:1e10, label: '> 10G'}, { value:1e11, label: '> 100G'}, { value:1e12, label: '> 1T'} ] + } + }, + { field: 'mom_object_group_id', + displayName: 'Group ID', + enableCellEdit: false, + cellTemplate:'<div style=\'text-align: center; padding-top:5px;\'>' + + '<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.mom_object_group_mom2object_id}}"' + + 'title="' + + 'Group name: ' + '{{row.entity.mom_object_group_name}}\n' + + 'Parent group name: ' + '{{row.entity.mom_object_parent_group_name}}\n' + + 'Parent group ID: ' + '{{row.entity.mom_object_parent_group_id}}' + + '">{{row.entity.mom_object_group_id}}</a></div>', + width: '80', + filter: { + condition: uiGridConstants.filter.EXACT, type: uiGridConstants.filter.SELECT, selectOptions: [] } @@ -72,14 +203,50 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid { field: 'mom_id', displayName: 'MoM ID', enableCellEdit: false, - cellTemplate:'<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.mom2object_id}}">{{row.entity[col.field]}}</a>', - width: '8%' + cellTemplate:'<div style=\'text-align: center; padding-top:5px;\'>' + + '<a target="_blank" href="https://lofar.astron.nl/mom3/user/project/setUpMom2ObjectDetails.do?view=generalinfo&mom2ObjectId={{row.entity.mom2object_id}}"' + + 'title="' + + 'Project description: ' + '{{row.grid.appScope.dataService.momProjectsDict[row.entity.project_mom_id].description}}\n' + + 'Task description: ' + '{{row.entity.description}}\n' + + 'Group name: ' + '{{row.entity.mom_object_group_name}}\n' + + 'Group ID: ' + '{{row.entity.mom_object_group_id}}\n' + + 'Parent group name: ' + '{{row.entity.mom_object_parent_group_name}}\n' + + 'Parent group ID: ' + '{{row.entity.mom_object_parent_group_id}}' + + '">{{row.entity[col.field]}} </a></div>', + width: '65' }, { field: 'otdb_id', displayName: 'SAS ID', enableCellEdit: false, - width: '8%' + cellTemplate:'<div style=\'text-align:center; padding-top:5px;\'>{{row.entity.otdb_id}}</div>', + width: '65' + }, + { field: 'id', + displayName: 'RADB ID', + enableCellEdit: false, + cellTemplate:'<div style=\'text-align:center; padding-top:5px;\'><a target="_blank" href="tasks/{{row.entity.id}}.html">{{row.entity[col.field]}}</a></div>', + width: '72' + }, + { field: 'cluster', + displayName: 'Cluster', + enableCellEdit: false, + width: '65', + filter: { + condition: uiGridConstants.filter.EXACT, + type: uiGridConstants.filter.SELECT, + selectOptions: [] + }, + cellClass: function(grid, row, col, rowRenderIndex, colRenderIndex) { + var value = grid.getCellValue(row,col); + return "grid-cluster-" + value; + }, + sort: { direction: uiGridConstants.ASC, priority: 1 } }]; + + if($scope.dataService.projectMode) { + $scope.columns.splice(1, 1); + } + $scope.gridOptions = { enableGridMenu: false, enableSorting: true, @@ -90,15 +257,77 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid enableRowSelection: true, enableRowHeaderSelection: true, enableFullRowSelection: false, + modifierKeysToMultiSelect: true, + multiSelect:true, enableSelectionBatchEvent:false, - multiSelect:false, gridMenuShowHideColumns: false, columnDefs: $scope.columns, data: [], +// rowTemplate: "<div ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.uid\" ui-grid-one-bind-id-grid=\"rowRenderIndex + '-' + col.uid + '-cell'\" class=\"ui-grid-cell\" ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader }\" role=\"{{col.isRowHeader ? 'rowheader' : 'gridcell'}}\" ui-grid-cell></div>" + rowTemplate: "<div ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.uid\" ui-grid-one-bind-id-grid=\"rowRenderIndex + '-' + col.uid + '-cell'\" class=\"ui-grid-cell\" ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader }\" role=\"{{col.isRowHeader ? 'rowheader' : 'gridcell'}}\" ui-grid-cell context-menu>", onRegisterApi: function(gridApi){ $scope.gridApi = gridApi; - $scope.gridApi.core.on.rowsRendered($scope, filterTasks); + $scope.gridApi.core.on.rowsRendered($scope, function() { + //on.rowsRendered is called whenever the data/filtering of the grid changed + //update the filteredTasks in the dataService from the resulting new grid rows + $scope.$evalAsync(function() { + var taskDict = $scope.dataService.taskDict; + $scope.dataService.filteredTasks = []; + var rows = $scope.gridApi.core.getVisibleRows(grid); + var numRows = rows.length; + for(var i = 0; i < numRows; i++) { + var row = rows[i]; + if(row.visible) + { + var task_id = row.entity.id; + var task = taskDict[task_id]; + if(task) { + $scope.dataService.filteredTasks.push(task); + } + + row.setSelected($scope.dataService.selected_task_ids.indexOf(task_id) != -1); + } + } + + $scope.dataService.filteredTaskDict = $scope.dataService.toIdBasedDict($scope.dataService.filteredTasks); + $scope.dataService.filteredTaskChangeCntr++; + + if($scope.dataService.filteredTasks.length == 0) { + var otdb_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'otdb_id'; }); + if(otdb_col && otdb_col.filters.length && otdb_col.filters[0].hasOwnProperty('term')) { + var otdb_id = otdb_col.filters[0].term; + $scope.$parent.$parent.loadTaskByOTDBIdSelectAndJumpIntoView(otdb_id).then(function() { + otdb_col.filters[0].term = null; + }); + } else { + var mom_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'mom_id'; }); + + if(mom_col && mom_col.filters.length && mom_col.filters[0].hasOwnProperty('term')) { + var mom_id = mom_col.filters[0].term; + $scope.$parent.$parent.loadTaskByMoMIdSelectAndJumpIntoView(mom_id).then(function(task) { + mom_col.filters[0].term = null; + if(task == undefined) { + //getting the task by mom_id did not find a task + //maybe the entered id was a mom group_id? + //let's try to loadTasksByMoMGroupIdSelectAndJumpIntoView + $scope.$parent.$parent.loadTasksByMoMGroupIdSelectAndJumpIntoView(mom_id).then(function(tasks) { + if(tasks == undefined || tasks.length == 0) { + //getting the tasks by mom group id did not find any tasks + //maybe the entered id was a mom parent group_id? + //let's try to loadTasksByMoMParentGroupIdSelectAndJumpIntoView + $scope.$parent.$parent.loadTasksByMoMParentGroupIdSelectAndJumpIntoView(mom_id).then(function(tasks) { + //pass + }); + } + }); + } + }); + } + } + } + }); + }); gridApi.edit.on.afterCellEdit($scope,function(rowEntity, colDef, newValue, oldValue){ var task = $scope.dataService.taskDict[rowEntity.id]; @@ -107,114 +336,199 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid }); gridApi.selection.on.rowSelectionChanged($scope,function(row){ - if(row.entity.id && row.isSelected) { - $scope.dataService.selected_task_id = row.entity.id; + if(row.entity.id) { + if(row.isSelected) { + $scope.dataService.addSelectedTaskId(row.entity.id); + } else if(!row.isSelected) { + $scope.dataService.removeSelectedTaskId(row.entity.id); + } } }); } }; - function filterTasks() { - var taskDict = $scope.dataService.taskDict; - var filteredTasks = []; - var filteredTaskDict = {}; - var rows = $scope.gridApi.grid.rows; - var numRows = rows.length; - for(var i = 0; i < numRows; i++) { - var row = rows[i]; - if(row.visible) - { - var task = taskDict[row.entity.id]; - filteredTasks.push(task); - filteredTaskDict[task.id] = task; - } - } - - $scope.dataService.filteredTasks = filteredTasks; - $scope.dataService.filteredTaskDict = filteredTaskDict; - }; - function fillColumFilterSelectOptions(options, columnDef) { - var columnSelectOptions = []; + if (columnDef == undefined) + return; + var columnSelectOptions = []; if(options) { - for(var i = 0; i < options.length; i++) - { + for(var i = 0; i < options.length; i++) { var option = options[i]; - columnSelectOptions.push({ value: option, label: option }) + if(option.hasOwnProperty('value') && option.hasOwnProperty('label')) { + columnSelectOptions.push({ value: option.value, label: option.label }) + } + else { + columnSelectOptions.push({ value: option, label: option }) + } } } columnDef.filter.selectOptions = columnSelectOptions; }; + function populateListAsync() { + var now = new Date(); + var diff = now.getTime() - self.lastUpdateTimestamp.getTime(); + if(diff > 750) { + self.waitingForDelayedUpdate = false; + $scope.$evalAsync(populateList); + } + else { + if (!self.waitingForDelayedUpdate) { + self.waitingForDelayedUpdate = true; + setTimeout(populateListAsync, diff); + } + } + }; + function populateList() { if('tasks' in $scope.dataService && $scope.dataService.tasks.length > 0) { var viewFrom = $scope.dataService.viewTimeSpan.from; var viewTo = $scope.dataService.viewTimeSpan.to; - var tasks = []; + $scope.dataService.filteredTasks = []; + var gridTasks = []; for(var task of $scope.dataService.tasks) { if(task.endtime >= viewFrom && task.starttime <= viewTo) { + $scope.dataService.filteredTasks.push(task); + var gridTask = { + blocked_by_ids: task.blocked_by_ids, + cluster: task.cluster, + description: task.description, + disk_usage: task.disk_usage, + disk_usage_readable: task.disk_usage_readable, + duration: task.duration, + endtime: task.endtime, id: task.id, - name: task.name, - project_name: task.project_name, + ingest_status: task.ingest_status, + mom2object_id: task.mom2object_id, mom_id: task.mom_id, + mom_object_group_id: task.mom_object_group_id, + mom_object_group_mom2object_id: task.mom_object_group_mom2object_id, + mom_object_group_name: task.mom_object_group_name, + mom_object_parent_group_id: task.mom_object_parent_group_id, + name: task.name, + nr_of_dataproducts: task.nr_of_dataproducts, otdb_id: task.otdb_id, + predecessor_ids: task.predecessor_ids, + project_mom2object_id: task.project_mom2object_id, + project_mom_id: task.project_mom_id, + project_name: task.project_name, + specification_id: task.specification_id, starttime: task.starttime, - endtime: task.endtime, - duration: task.duration, status: task.status, + status_id: task.status_id, + sub_type: task.sub_type, + successor_ids: task.successor_ids, type: task.type, - project_mom2object_id: task.project_mom2object_id, - mom2object_id: task.mom2object_id + type_id: task.type_id }; - tasks.push(gridTask); + + + + gridTasks.push(task); } } - $scope.gridOptions.data = tasks; + $scope.gridOptions.data = gridTasks; } else $scope.gridOptions.data = [] - fillProjectsColumFilterSelectOptions(); + fillProjectsColumFilterSelectOptions() + fillGroupsColumFilterSelectOptions(); + self.lastUpdateTimestamp = new Date(); }; - function jumpToSelectedTaskRow() { - var taskIdx = $scope.gridOptions.data.findIndex(function(row) {return row.id == dataService.selected_task_id}); + function jumpToSelectedTaskRows() { + var rowIndices = dataService.selected_task_ids.map(function(t_id) { return $scope.gridOptions.data.findIndex(function(row) {return row.id == t_id; } ); }); + rowIndices = rowIndices.filter(function(idx) {return idx > -1;}).sort(); - if(taskIdx > -1) { - $scope.gridApi.selection.selectRow($scope.gridOptions.data[taskIdx]); - $scope.gridApi.core.scrollTo($scope.gridOptions.data[taskIdx], null); + for(var rowIndex of rowIndices) { + $scope.gridApi.core.scrollTo($scope.gridOptions.data[rowIndex], null); } }; - $scope.$watch('dataService.taskChangeCntr', populateList); + function onSelectedTaskIdsChanged() { + var selected_task_ids = $scope.dataService.selected_task_ids; + var rows = $scope.gridApi.grid.rows; + + for(var row of rows) { + row.setSelected(selected_task_ids.indexOf(row.entity.id) != -1); + } + + //find out if we have selected all tasks in a single mom group + var selected_tasks = $scope.dataService.selected_task_ids.map(function(t_id) { return $scope.dataService.taskDict[t_id]; }).filter(function(t) { return t != undefined;}); + + if(selected_tasks && selected_tasks.length > 1) { + var selected_task_group_ids = selected_tasks.map(function(t) { return t.mom_object_group_id; }); + selected_task_group_ids = selected_task_group_ids.unique(); + + if(selected_task_group_ids.length == 1) { + //we have selected tasks in a single mom group + //find out if we have selected all tasks within this mom group + var mom_object_group_id = selected_task_group_ids[0]; + var all_group_tasks = $scope.dataService.tasks.filter(function(t) { return t.mom_object_group_id == mom_object_group_id; }); + + if(all_group_tasks.length == selected_tasks.length) { + //we have selected all tasks in a single mom group + //apply filter on group column to see only tasks within this group + var group_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'mom_object_group_id'; }); + if(group_col) { + var mom_object_group_name = all_group_tasks[0].mom_object_group_name; + var label = mom_object_group_id + ' ' + mom_object_group_name; + + var groupSelectOptions = [ { value: mom_object_group_id, label: label} ]; + + fillColumFilterSelectOptions(groupSelectOptions, $scope.columns.find(function(c) {return c.field == 'mom_object_group_id'; })); + group_col.filters[0].term = mom_object_group_id; + } + } + } + } + + + $scope.$evalAsync(jumpToSelectedTaskRows); + }; + + $scope.$watch('dataService.taskChangeCntr', function() { populateListAsync(); }); + $scope.$watch('dataService.claimChangeCntr', function() { populateListAsync(); }); $scope.$watch('dataService.viewTimeSpan', function() { - populateList(); - setTimeout(jumpToSelectedTaskRow, 250); + populateListAsync(); + $scope.$evalAsync(jumpToSelectedTaskRows); }, true); - $scope.$watch('dataService.taskstatustypes', function() { - taskstatustypenames = $scope.dataService.taskstatustypes.map(function(x) { return x.name; }); - fillColumFilterSelectOptions(taskstatustypenames, $scope.columns[5]); - $scope.columns[6].editDropdownOptionsArray = $scope.dataService.taskstatustypes.map(function(x) { return {id:x.name, value:x.name}; }); - }); + function fillFilterSelectOptions() { + if(dataService.initialLoadComplete) { + fillStatusColumFilterSelectOptions(); + fillInfoColumFilterSelectOptions(); + fillTypeColumFilterSelectOptions(); + fillProjectsColumFilterSelectOptions(); + fillGroupsColumFilterSelectOptions(); + fillColumFilterSelectOptions(['CEP2', 'CEP4'], $scope.columns.find(function(c) {return c.field == 'cluster'; })); + } + }; - $scope.$watch('dataService.tasktypes', function() { - tasktypenames = $scope.dataService.tasktypes.map(function(x) { return x.name; }); - fillColumFilterSelectOptions(tasktypenames, $scope.columns[6]); - }); + $scope.$watch('dataService.filteredTaskChangeCntr', function() { $scope.$evalAsync(fillFilterSelectOptions()); }); + $scope.$watch('dataService.initialLoadComplete', function() { + populateListAsync(); + $scope.$evalAsync(fillFilterSelectOptions()); }); function fillProjectsColumFilterSelectOptions() { var projectNames = []; var momProjectsDict = $scope.dataService.momProjectsDict; - var tasks = $scope.dataService.tasks; + var tasks = $scope.dataService.filteredTasks; + + var project_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'project_name'; }); + if(project_col && project_col.filters.length && project_col.filters[0].term) { + tasks = $scope.dataService.tasks; + } + //get unique projectIds from tasks var task_project_ids = tasks.map(function(t) { return t.project_mom_id; }); - task_project_ids = task_project_ids.filter(function(value, index, arr) { return arr.indexOf(value) == index;}) + task_project_ids = task_project_ids.unique(); for(var project_id of task_project_ids) { if(momProjectsDict.hasOwnProperty(project_id)) { @@ -225,10 +539,364 @@ gridControllerMod.controller('GridController', ['$scope', 'dataService', 'uiGrid } } projectNames.sort(); - fillColumFilterSelectOptions(projectNames, $scope.columns[1]); + fillColumFilterSelectOptions(projectNames, $scope.columns.find(function(c) {return c.field == 'project_name'; })); + }; + + function fillStatusColumFilterSelectOptions() { + var tasks = $scope.dataService.filteredTasks; + + var status_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'status'; }); + if(status_col && status_col.filters.length && status_col.filters[0].term) { + tasks = $scope.dataService.tasks; + } + + //get unique statuses from tasks + var task_statuses = tasks.map(function(t) { return t.status; }); + task_statuses = task_statuses.unique(); + task_statuses.sort(); + + fillColumFilterSelectOptions(task_statuses, $scope.columns.find(function(c) {return c.field == 'status'; })); + }; + + function fillTypeColumFilterSelectOptions() { + var tasks = $scope.dataService.filteredTasks; + + var type_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'type'; }); + if(type_col && type_col.filters.length && type_col.filters[0].term) { + tasks = $scope.dataService.tasks; + } + + //get unique types from tasks + var task_types = tasks.map(function(t) { return t.type; }); + task_types = task_types.unique(); + task_types.sort(); + + fillColumFilterSelectOptions(task_types, $scope.columns.find(function(c) {return c.field == 'type'; })); + }; + + function fillGroupsColumFilterSelectOptions() { + var group_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'mom_object_group_id'; }); + if(!group_col || group_col.filter.term) { + return; + } + + var tasks = $scope.dataService.filteredTasks; + + //get unique groupNames from tasks + var groupId2Name = {}; + var groupIds = []; + + for(var task of tasks) { + if(task.mom_object_group_id) { + if(!groupId2Name.hasOwnProperty(task.mom_object_group_id)) { + groupId2Name[task.mom_object_group_id] = task.mom_object_group_name; + groupIds.push(task.mom_object_group_id); + } + } + } + + groupIds.sort(); + + fillColumFilterSelectOptions(groupIds, $scope.columns.find(function(c) {return c.field == 'mom_object_group_id'; })); }; - $scope.$watch('dataService.momProjectsDict', fillProjectsColumFilterSelectOptions); - $scope.$watch('dataService.selected_task_id', jumpToSelectedTaskRow); -} -]); + function fillInfoColumFilterSelectOptions() { + var tasks = $scope.dataService.filteredTasks; + + var info_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'info'; }); + if(info_col && info_col.filters.length && info_col.filters[0].term) { + tasks = $scope.dataService.tasks; + } + + // Generate a list of unique information items + var task_info = []; + var info_bit_flags = 0x00; + for(var task of tasks) { + if((task.blocked_by_ids.length > 0) && !(info_bit_flags & 0x01)) { + task_info.push({ value: 0, label: 'Blocked tasks' }); + info_bit_flags |= 0x01; + } + + if((task.ingest_status === 'ingesting') && !(info_bit_flags & 0x02)) { + task_info.push({ value: 1, label: 'Ingests in progress' }); + info_bit_flags |= 0x02; + } + + if((task.ingest_status === 'ingested') && !(info_bit_flags & 0x04)) { + task_info.push({ value: 2, label: 'Successful ingests' }); + info_bit_flags |= 0x04; + } + + if((task.ingest_status === 'failed') && !(info_bit_flags & 0x08)) { + task_info.push({ value: 3, label: 'Failed ingests' }); + info_bit_flags |= 0x08; + } + }; + + // sort on key values + function keysrt(key,desc) { + return function(a,b){ + return desc ? ~~(a[key] < b[key]) : ~~(a[key] > b[key]); + } + } + + task_info.sort(keysrt('value')); + fillColumFilterSelectOptions(task_info, $scope.columns.find(function(c) {return c.field == 'info'; })); + }; + + $scope.$watch('dataService.selected_task_ids', onSelectedTaskIdsChanged, true); + $scope.$watch('dataService.selected_project_id', function() { + fillProjectsColumFilterSelectOptions(); + + var project_col = $scope.gridApi.grid.columns.find(function(c) {return c.field == 'project_name'; }); + if(project_col && project_col.filters.length) { + if(dataService.selected_project_id != undefined) { + var projectName = dataService.momProjectsDict[dataService.selected_project_id].name; + if(projectName != undefined) { + var project_names = project_col.filter.selectOptions.map(function(so) { return so.value;}); + if(project_names.includes(projectName)) { + project_col.filters[0].term = projectName; + } + } + } + } + }); +}]); + +gridControllerMod.directive('contextMenu', ['$document', '$window', function($document, $window) { + return { + restrict: 'A', + scope: { + }, + link: function($scope, $element, $attrs) { + function handleContextMenuEvent(event) { + //pragmatic 'hard-coded' way of getting the dataService and the rowEntity via scope tree. + var dataService = $scope.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent.dataService; + var cleanupCtrl = $scope.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent.cleanupCtrl; + var dataCtrlScope = $scope.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent.$parent; + var row = $scope.$parent.$parent.$parent.row; + var rowEntity = row.entity; + + if(!dataService || !rowEntity) + return true; + + var taskId = rowEntity.id; + var task = dataService.taskDict[taskId]; + if(!task) + return true; + + if(!dataService.isTaskIdSelected(taskId)) { + dataService.setSelectedTaskId(taskId); + } + + var docElement = angular.element($document); + + //search for already existing contextmenu element + while($document.find('#grid-context-menu').length) { + //found, remove it, so we can create a fresh one + $document.find('#grid-context-menu')[0].remove(); + + //unbind document close event handlers + docElement.unbind('click', closeContextMenu); + docElement.unbind('contextmenu', closeContextMenu); + } + + //create contextmenu element + //with list of menu items, + //each with it's own action + var contextmenuElement = angular.element('<div id="grid-context-menu"></div>'); + var ulElement = angular.element('<ul class="dropdown-menu" role="menu" style="left:' + event.clientX + 'px; top:' + event.clientY + 'px; z-index: 100000; display:block;"></ul>'); + contextmenuElement.append(ulElement); + + var selected_tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); + selected_tasks = selected_tasks.filter(function(t) { return t != undefined; }); + var selected_cep4_tasks = selected_tasks.filter(function(t) { return t['cluster'] == 'CEP4'; }); + +// var liElement = angular.element('<li><a href="#">Copy Task</a></li>'); +// ulElement.append(liElement); +// liElement.on('click', function() { +// closeContextMenu(); +// //TODO: remove link to dataService in this generic plugin +// dataService.copyTask(task); +// }); + + var liElement = angular.element('<li><a href="#">Select group</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + dataCtrlScope.loadTasksByMoMGroupIdSelectAndJumpIntoView(task.mom_object_group_id); + }); + + var liElement = angular.element('<li><a href="#">Select parent group</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + dataCtrlScope.loadTasksByMoMParentGroupIdSelectAndJumpIntoView(task.mom_object_parent_group_id); + }); + + var blocked_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return (t.blocked_by_ids.length > 0); }); + + if(blocked_selected_cep4_tasks.length > 0) { + var liContent = '<li><a href="#">Select blocking predecessor(s)</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + + var blocking_predecessors = [] + for(var task of blocked_selected_cep4_tasks) { + blocking_predecessors = blocking_predecessors.concat(task.blocked_by_ids); + } + row.grid.appScope.selectBlockingPredecessors(blocking_predecessors); + }); + } + + var completed_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return t.status == 'finished' || t.status == 'aborted'; }); + var completed_selected_cep4_observations = completed_selected_cep4_tasks.filter(function(t) { return t.type == 'observation'; }); + + if(completed_selected_cep4_observations.length > 0 && dataService.config.inspection_plots_base_url) { + var liElement = angular.element('<li><a href="#">Inspection Plots</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + + var window_cntr = 0; + for(var obs of completed_selected_cep4_observations) { + var url = dataService.config.inspection_plots_base_url + '/' + obs.otdb_id; + url = row.grid.appScope.sanitize_url(url); + setTimeout(function(url_arg) { + $window.open(url_arg, '_blank'); + }, window_cntr*750, url); + window_cntr += 1; + } + }); + } + + var ingest_tasks = selected_tasks.filter(function(t) { return t.ingest_status != undefined; }); + + if(ingest_tasks.length > 0 && dataService.config.lta_base_url) { + var liElement = angular.element('<li><a href="#">Open in LTA catalogue</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + row.grid.appScope.openLtaLocation(ingest_tasks); + }); + } + + var liContent = completed_selected_cep4_tasks.length > 0 ? '<li><a href="#">Show disk usage</a></li>' : '<li><a href="#" style="color:#aaaaaa">Show disk usage</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + if(completed_selected_cep4_tasks.length > 0) { + liElement.on('click', function() { + closeContextMenu(); + if(selected_cep4_tasks.length == 1) { + cleanupCtrl.showTaskDiskUsage(task); + } else { + cleanupCtrl.showTaskDiskUsage(completed_selected_cep4_tasks); + } + }); + } + + var liContent = completed_selected_cep4_tasks.length > 0 ? '<li><a href="#">Delete data</a></li>' : '<li><a href="#" style="color:#aaaaaa">Delete data</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + if(completed_selected_cep4_tasks.length > 0) { + liElement.on('click', function() { + closeContextMenu(); + cleanupCtrl.deleteTasksDataWithConfirmation(completed_selected_cep4_tasks); + }); + } + + var approved_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return t.status == 'approved' && t.type == 'pipeline'; }); + + if(approved_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Schedule approved CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of approved_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'prescheduled' }; + dataService.putTask(newTask); + } + }); + } + + var scheduled_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'prescheduled' || t.status == 'scheduled' || t.status == 'queued') && t.type == 'pipeline'; }); + + if(scheduled_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Unschedule (pre)scheduled/queued CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of scheduled_selected_cep4_pipelines) { + if(pl.status == 'queued') { + var newTask = { id: pl.id, status: 'aborted' }; + dataService.putTask(newTask); + } + + var newTask = { id: pl.id, status: 'approved' }; + dataService.putTask(newTask); + } + }); + } + + var active_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'active' || t.status == 'completing') && t.type == 'pipeline'; }); + + if(active_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Abort active CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of active_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'aborted' }; + dataService.putTask(newTask); + } + }); + } + + var aborted_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'aborted' || t.status == 'error') && t.type == 'pipeline'; }); + + if(aborted_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Reschedule aborted/error CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of aborted_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'prescheduled' }; + dataService.putTask(newTask); + } + }); + } + + var closeContextMenu = function(cme) { + contextmenuElement.remove(); + + //unbind document close event handlers + docElement.unbind('click', closeContextMenu); + docElement.unbind('contextmenu', closeContextMenu); + }; + + //click anywhere to remove the contextmenu + docElement.bind('click', closeContextMenu); + docElement.bind('contextmenu', closeContextMenu); + + //add contextmenu to body + var body = $document.find('body'); + body.append(contextmenuElement); + + //prevent bubbling event upwards + return false; + } + + $element.bind('contextmenu', handleContextMenuEvent); + + $scope.$on('$destroy', function() { + $element.unbind('contextmenu', handleContextMenuEvent); + }); + } + }; + }]); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js new file mode 100644 index 0000000000000000000000000000000000000000..bdbd8603707f319d6a8eb27176f2d9f7fc1e0a06 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js @@ -0,0 +1,249 @@ +(function(){ + 'use strict'; + angular.module('gantt.contextmenu', ['gantt', 'gantt.contextmenu.templates']).directive('ganttContextmenu', ['$compile', '$document', '$window', function($compile, $document, $window) { + return { + restrict: 'E', + require: '^gantt', + scope: { + enabled: '=?' + }, + link: function(scope, element, attrs, ganttCtrl) { + var api = ganttCtrl.gantt.api; + + // Load options from global options attribute. + if (scope.options && typeof(scope.options.contextmenu) === 'object') { + for (var option in scope.options.contextmenu) { + scope[option] = scope.options[option]; + } + } + + if (scope.enabled === undefined) { + scope.enabled = true; + } + + api.directives.on.new(scope, function(dName, dScope, dElement, dAttrs, dController) { + //for each new ganttTask + if (dName === 'ganttTask') { + dElement.bind('contextmenu', function(event) { + //TODO: remove link to dataService in this generic plugin + var dataService = dScope.scope.dataService; + var dataCtrlScope = dScope.scope.$parent.$parent; + var cleanupCtrl = dScope.scope.$parent.cleanupCtrl; + var docElement = angular.element($document); + + var task = dScope.task.model.raTask; + + if(!task) + return; + + if(!dataService.isTaskIdSelected(task.id)) { + dataService.setSelectedTaskId(task.id); + } + + //search for already existing contextmenu element + while($document.find('#gantt-context-menu').length) { + //found, remove it, so we can create a fresh one + $document.find('#gantt-context-menu')[0].remove(); + + //unbind document close event handlers + docElement.unbind('click', closeContextMenu); + docElement.unbind('contextmenu', closeContextMenu); + } + + //create contextmenu element + //with list of menu items, + //each with it's own action + var contextmenuElement = angular.element('<div id="gantt-context-menu"></div>'); + var ulElement = angular.element('<ul class="dropdown-menu" role="menu" style="left:' + event.clientX + 'px; top:' + event.clientY + 'px; z-index: 100000; display:block;"></ul>'); + contextmenuElement.append(ulElement); + + var selected_tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); + selected_tasks = selected_tasks.filter(function(t) { return t != undefined; }); + var selected_cep4_tasks = selected_tasks.filter(function(t) { return t['cluster'] == 'CEP4'; }); + +// var liElement = angular.element('<li><a href="#">Copy Task</a></li>'); +// ulElement.append(liElement); +// liElement.on('click', function() { +// closeContextMenu(); +// //TODO: remove link to dataService in this generic plugin +// dataService.copyTask(task); +// }); + + var liElement = angular.element('<li><a href="#">Select group</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + dataCtrlScope.loadTasksByMoMGroupIdSelectAndJumpIntoView(task.mom_object_group_id); + }); + + var liElement = angular.element('<li><a href="#">Select parent group</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + dataCtrlScope.loadTasksByMoMParentGroupIdSelectAndJumpIntoView(task.mom_object_parent_group_id); + }); + + var blocked_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return (t.blocked_by_ids.length > 0); }); + + if(blocked_selected_cep4_tasks.length > 0) { + var liContent = '<li><a href="#">Select blocking predecessors</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + + var blocking_predecessors = [] + for(var task of blocked_selected_cep4_tasks) { + blocking_predecessors = blocking_predecessors.concat(task.blocked_by_ids); + } + + dataCtrlScope.loadTasksSelectAndJumpIntoView(blocking_predecessors); + }); + } + + var completed_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return t.status == 'finished' || t.status == 'aborted'; }); + var completed_selected_cep4_observations = completed_selected_cep4_tasks.filter(function(t) { return t.type == 'observation'; }); + + if(completed_selected_cep4_observations.length > 0 && dataService.config.inspection_plots_base_url) { + var liElement = angular.element('<li><a href="#">Inspection Plots</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + + var window_cntr = 0; + for(var obs of completed_selected_cep4_observations) { + var url = dataService.config.inspection_plots_base_url + '/' + obs.otdb_id; + setTimeout(function(url_arg) { + $window.open(url_arg, '_blank'); + }, window_cntr*750, url); + window_cntr += 1; + } + }); + } + + var liContent = completed_selected_cep4_tasks.length > 0 ? '<li><a href="#">Show disk usage</a></li>' : '<li><a href="#" style="color:#aaaaaa">Show disk usage</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + if(completed_selected_cep4_tasks.length > 0) { + liElement.on('click', function() { + closeContextMenu(); + if(selected_cep4_tasks.length == 1) { + cleanupCtrl.showTaskDiskUsage(task); + } else { + cleanupCtrl.showTaskDiskUsage(completed_selected_cep4_tasks); + } + }); + } + + var liContent = completed_selected_cep4_tasks.length > 0 ? '<li><a href="#">Delete data</a></li>' : '<li><a href="#" style="color:#aaaaaa">Delete data</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + if(completed_selected_cep4_tasks.length > 0) { + liElement.on('click', function() { + closeContextMenu(); + cleanupCtrl.deleteTasksDataWithConfirmation(completed_selected_cep4_tasks); + }); + } + + var approved_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return t.status == 'approved' && t.type == 'pipeline'; }); + + if(approved_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Schedule approved CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of approved_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'prescheduled' }; + dataService.putTask(newTask); + } + }); + } + + var scheduled_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'prescheduled' || t.status == 'scheduled' || t.status == 'queued') && t.type == 'pipeline'; }); + + if(scheduled_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Unschedule (pre)scheduled/queued CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of scheduled_selected_cep4_pipelines) { + if(pl.status == 'queued') { + var newTask = { id: pl.id, status: 'aborted' }; + dataService.putTask(newTask); + } + + var newTask = { id: pl.id, status: 'approved' }; + dataService.putTask(newTask); + } + }); + } + + var active_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'active' || t.status == 'completing') && t.type == 'pipeline'; }); + + if(active_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Abort active CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of active_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'aborted' }; + dataService.putTask(newTask); + } + }); + } + + var aborted_selected_cep4_pipelines = selected_cep4_tasks.filter(function(t) { return (t.status == 'aborted' || t.status == 'error') && t.type == 'pipeline'; }); + + if(aborted_selected_cep4_pipelines.length > 0) { + var liContent = '<li><a href="#">Reschedule aborted/error CEP4 pipelines</a></li>' + var liElement = angular.element(liContent); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + for(var pl of aborted_selected_cep4_pipelines) { + var newTask = { id: pl.id, status: 'prescheduled' }; + dataService.putTask(newTask); + } + }); + } + + var closeContextMenu = function() { + contextmenuElement.remove(); + + //unbind document close event handlers + docElement.unbind('click', closeContextMenu); + docElement.unbind('contextmenu', closeContextMenu); + }; + + //click anywhere to remove the contextmenu + docElement.bind('click', closeContextMenu); + docElement.bind('contextmenu', closeContextMenu); + + //add contextmenu to body + var body = $document.find('body'); + body.append(contextmenuElement); + + //prevent bubbling event upwards + return false; + }); + } + }); + + api.directives.on.destroy(scope, function(dName, dScope, dElement, dAttrs, dController) { + //for each destroyed ganttTask + if (dName === 'ganttTask') { + dElement.unbind('contextmenu'); + } + }); + } + }; + }]); +}()); + +angular.module('gantt.contextmenu.templates', []).run(['$templateCache', function($templateCache) { + +}]); + diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css index 2abc793dd109eb8c1d970c65ed641eec7c7fa3ec..0326516672c9dcda2dffb60c5990657bb7a98b01 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css @@ -1,5 +1,10 @@ /* $Id: app.js 32724 2015-10-28 13:24:49Z schaap $ */ +body { + font-family: DejaVu Sans Monospace; + font-size: 12px; +} + .grid { min-height: 400px; min-width: 500px; @@ -17,17 +22,26 @@ } .dropdown-menu { - z-index: 10000; + z-index: 1010; +} + +.md-dialog-container { + z-index: 1000; } .ui-grid-cell { overflow: visible; + padding: 0px 2px; } .datepicker-wrapper ul { /* visibility: hidden; */ } +.ui-splitbar { + z-index: 1000; +} + .ui-layout-row > .ui-splitbar { height: 6px; background-color: #CCCCCC; @@ -53,15 +67,28 @@ table.uib-timepicker td.uib-time { } .gantt-side { - width: 150px; + width: 180px; } .top-stretch { top: 65px; } +.modal-content { + width: 105% +} + +.modal-dialog { + width: 960px +} + +.md-dialog-content { + font-size: 14px; +} + .gantt-task-content { - margin-top: 2.2px; +/* margin-top: 2.2px; */ + padding-top: 2.2px; } .gantt-task.task-selected-task { @@ -83,60 +110,131 @@ div.gantt-task span { padding: 0px 10px; } -div.gantt-task.task-status-on_hold span, div.gantt-task.task-status-prescheduled span, div.gantt-task.task-status-scheduled span, div.gantt-task.task-status-queued span, div.gantt-task.task-status-aborted span, div.gantt-task.task-status-error span { - color: #ffffff; -} -div.gantt-task.claim-task-status-on_hold span, div.gantt-task.claim-task-status-prescheduled span, div.gantt-task.claim-task-status-scheduled span, div.gantt-task.claim-task-status-queued span, div.gantt-task.claim-task-status-aborted span, div.gantt-task.claim-task-status-error span { - color: #ffffff; +/* + The same status coloring is used for both the grid items and the gantt-chart items (a.k.a. tasks) +*/ + +.grid-status-prepared, +div.gantt-task.task-status-prepared div, +div.gantt-task.claim-task-status-prepared span { + background-color: #cccccc !important; } +.grid-status-opened, +div.gantt-task.task-status-opened div, +div.gantt-task.claim-task-status-opened span { + background-color: #aaaaaa !important; +} -div.gantt-task.claim-task-status-prepared span { - background: #cccccc; +.grid-status-suspended, +div.gantt-task.task-status-suspended div, +div.gantt-task.claim-task-status-suspended span { + background-color: #776666 !important; } +.grid-status-approved, +div.gantt-task.task-status-approved div, div.gantt-task.claim-task-status-approved span { - background: #8cb3d9; + background-color: #8cb3d9 !important; } -div.gantt-task.claim-task-status-on_hold span { - background: #b34700; +.grid-status-on_hold, +div.gantt-task.task-status-on_hold div, +div.gantt-task.claim-task-status-on_hold span { + background-color: #b34700 !important; + color: #ffffff; } +.grid-status-conflict, +div.gantt-task.task-status-conflict div, div.gantt-task.claim-task-status-conflict span { - background: #ff0000; + background-color: #ff0000 !important; } +.grid-status-prescheduled, +div.gantt-task.task-status-prescheduled div, div.gantt-task.claim-task-status-prescheduled span { - background: #6666ff; + background-color: #6666ff !important; + color: #ffffff; } +.grid-status-scheduled, .grid-status-blocked, +div.gantt-task.task-status-scheduled div, +div.gantt-task.task-status-blocked div, div.gantt-task.claim-task-status-scheduled span { - background: #0000ff; + background-color: #0000ff !important; + color: #ffffff; } +.grid-status-queued, +div.gantt-task.task-status-queued div, div.gantt-task.claim-task-status-queued span { - background: #ccffff; + background-color: #ccffff !important; } +.grid-status-active, +div.gantt-task.task-status-active div, div.gantt-task.claim-task-status-active span { - background: #ffff00; + background-color: #ffff00 !important; } +.grid-status-completing, +div.gantt-task.task-status-completing div, div.gantt-task.claim-task-status-completing span { - background: #ffdd99; + background-color: #ffdd99 !important; } +.grid-status-finished, +div.gantt-task.task-status-finished div, div.gantt-task.claim-task-status-finished span { - background: #00ff00; + background-color: #00ff00 !important; } +.grid-status-aborted, +div.gantt-task.task-status-aborted div, div.gantt-task.claim-task-status-aborted span { - background: #cc0000; + background-color: #cc0000 !important; + color: #ffffff; } +.grid-status-error, +div.gantt-task.task-status-error div, div.gantt-task.claim-task-status-error span { - background: #990033; + background-color: #990033 !important; + color: #ffffff; +} + +.grid-status-obsolete, +div.gantt-task.task-status-obsolete div, +div.gantt-task.claim-task-status-obsolete span { + background-color: #bcb39c !important; +} + +div.gantt-task.task-status-blocked div { +/* background-image: url(/static/icons/blocked.png); + background-repeat: no-repeat; */ +} + +div.gantt-task.task-status-blocked div{ + background-position: left 50%; +} + + +.grid-cluster-CEP2 { + background-color: #ccffff !important; } +.grid-cluster-CEP4 { + background-color: #ccffcc !important; +} + + + + + + + + + + diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/blocked.png b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/blocked.png new file mode 100644 index 0000000000000000000000000000000000000000..764ba9e1aad1438911a9d1482195a47d7f778232 Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/blocked.png differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_failed.png b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_failed.png new file mode 100644 index 0000000000000000000000000000000000000000..287ad48c9f6b973618148f6dcd3d82957c5fbd59 Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_failed.png differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_in_progress.png b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_in_progress.png new file mode 100644 index 0000000000000000000000000000000000000000..c23a0c887ce8c2f9ad8a9218fe61b7a9ee3449dc Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_in_progress.png differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_successful.png b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_successful.png new file mode 100644 index 0000000000000000000000000000000000000000..e36932e3fdced37b333842bd94b4d0224ec7a9aa Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/ingest_successful.png differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.jpg b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9deadcface0a83e3c2825ad039cb31b247c29ab7 Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.jpg differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.png b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.png new file mode 100644 index 0000000000000000000000000000000000000000..4e700ae9a0feddd82aa55dec0ba9fe0720ebec68 Binary files /dev/null and b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/icons/src/iconset.png differ diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-animate/angular-animate.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-animate/angular-animate.min.js new file mode 100644 index 0000000000000000000000000000000000000000..a7d2f272bfac592498fd7b141bb51d2b4859b94d --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-animate/angular-animate.min.js @@ -0,0 +1,56 @@ +/* + AngularJS v1.5.5 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(S,q){'use strict';function Aa(a,b,c){if(!a)throw Ma("areq",b||"?",c||"required");return a}function Ba(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function Na(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function X(a,b,c){var d="";a=ba(a)?a:a&&P(a)&&a.length?a.split(/\s+/):[];r(a,function(a,f){a&&0<a.length&&(d+=0<f?" ":"",d+=c?b+a:a+b)});return d}function Oa(a){if(a instanceof G)switch(a.length){case 0:return[]; +case 1:if(1===a[0].nodeType)return a;break;default:return G(ca(a))}if(1===a.nodeType)return G(a)}function ca(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Pa(a,b,c){r(b,function(b){a.addClass(b,c)})}function Qa(a,b,c){r(b,function(b){a.removeClass(b,c)})}function U(a){return function(b,c){c.addClass&&(Pa(a,b,c.addClass),c.addClass=null);c.removeClass&&(Qa(a,b,c.removeClass),c.removeClass=null)}}function pa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation|| +Q;a.domOperation=function(){a.$$domOperationFired=!0;b();b=Q};a.$$prepared=!0}return a}function ga(a,b){Ca(a,b);Da(a,b)}function Ca(a,b){b.from&&(a.css(b.from),b.from=null)}function Da(a,b){b.to&&(a.css(b.to),b.to=null)}function V(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),f=(d.removeClass||"")+" "+(c.removeClass||"");a=Ra(a.attr("class"),e,f);c.preparationClasses&&(d.preparationClasses=Y(c.preparationClasses,d.preparationClasses),delete c.preparationClasses); +e=d.domOperation!==Q?d.domOperation:null;Ea(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Ra(a,b,c){function d(a){P(a)&&(a=a.split(" "));var b={};r(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);r(b,function(a,b){e[b]=1});c=d(c);r(c,function(a,b){e[b]=1===e[b]?null:-1});var f={addClass:"",removeClass:""};r(e,function(b,c){var d,e;1===b?(d="addClass", +e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(f[d].length&&(f[d]+=" "),f[d]+=c)});return f}function D(a){return a instanceof q.element?a[0]:a}function Sa(a,b,c){var d="";b&&(d=X(b,"ng-",!0));c.addClass&&(d=Y(d,X(c.addClass,"-add")));c.removeClass&&(d=Y(d,X(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function qa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ta(a,b){var c=b?"paused":"",d=Z+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]= +b[1]}function Y(a,b){return a?b?a+" "+b:a:b}function Fa(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};r(c,function(a,b){var c=e[a];if(c){var s=c.charAt(0);if("-"===s||"+"===s||0<=s)c=Ta(c);0===c&&(c=null);d[b]=c}});return d}function Ta(a){var b=0;a=a.split(/\s*,\s*/);r(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function ua(a){return 0===a||null!=a}function Ga(a,b){var c=T,d=a+"s";b?c+="Duration":d+=" linear all"; +return[c,d]}function Ha(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ia(a,b,c){r(c,function(c){a[c]=da(a[c])?a[c]:b.style.getPropertyValue(c)})}var Q=q.noop,Ja=q.copy,Ea=q.extend,G=q.element,r=q.forEach,ba=q.isArray,P=q.isString,va=q.isObject,C=q.isUndefined,da=q.isDefined,Ka=q.isFunction,wa=q.isElement,T,xa,Z,ya;C(S.ontransitionend)&& +da(S.onwebkittransitionend)?(T="WebkitTransition",xa="webkitTransitionEnd transitionend"):(T="transition",xa="transitionend");C(S.onanimationend)&&da(S.onwebkitanimationend)?(Z="WebkitAnimation",ya="webkitAnimationEnd animationend"):(Z="animation",ya="animationend");var ra=Z+"Delay",za=Z+"Duration",ma=T+"Delay",La=T+"Duration",Ma=q.$$minErr("ng"),Ua={transitionDuration:La,transitionDelay:ma,transitionProperty:T+"Property",animationDuration:za,animationDelay:ra,animationIterationCount:Z+"IterationCount"}, +Va={transitionDuration:La,transitionDelay:ma,animationDuration:za,animationDelay:ra};q.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,f,z){var B,s;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){B&&a.leave(B);s&&(s.$destroy(),s=null);if(e||0===e)s=b.$new(),z(s,function(b){B=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b, +c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var f=d.ngAnimateChildren;q.isString(f)&&0===f.length?c.data("$$ngAnimateChildren",!0):(e(a(f)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),z=0;z<b.length;z++)b[z]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue", +["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);r(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return f[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var f=this.rules={skip:[],cancel:[],join:[]};f.join.push(function(a,b,c){return!b.structural&&e(b)});f.skip.push(function(a, +b,c){return!b.structural&&!e(b)});f.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});f.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});f.cancel.push(function(a,b,c){return c.structural&&b.structural});f.cancel.push(function(a,b,c){return 2===c.state&&b.structural});f.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return C(a)&&C(b)||C(e)&&C(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope", +"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,f,v,I,Wa,u,sa,w,x){function R(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function J(a,b,c){var g=D(b),d=D(a),k=[];(a=h[c])&&r(a,function(a){ia.call(a.node,g)?k.push(a.callback):"leave"===c&&ia.call(a.node,d)&&k.push(a.callback)});return k}function k(a,b,c){var g=ca(b);return a.filter(function(a){return!(a.node===g&&(!c||a.callback===c))})} +function p(a,k,h){function l(c,g,d,h){f(function(){var c=J(oa,a,g);c.length?b(function(){r(c,function(b){b(a,d,h)});"close"!==d||a[0].parentNode||N.off(a)}):"close"!==d||a[0].parentNode||N.off(a)});c.progress(g,d,h)}function A(b){var c=a,g=m;g.preparationClasses&&(c.removeClass(g.preparationClasses),g.preparationClasses=null);g.activeClasses&&(c.removeClass(g.activeClasses),g.activeClasses=null);F(a,m);ga(a,m);m.domOperation();p.complete(!b)}var m=Ja(h),x,oa;if(a=Oa(a))x=D(a),oa=a.parent();var m= +pa(m),p=new u,f=R();ba(m.addClass)&&(m.addClass=m.addClass.join(" "));m.addClass&&!P(m.addClass)&&(m.addClass=null);ba(m.removeClass)&&(m.removeClass=m.removeClass.join(" "));m.removeClass&&!P(m.removeClass)&&(m.removeClass=null);m.from&&!va(m.from)&&(m.from=null);m.to&&!va(m.to)&&(m.to=null);if(!x)return A(),p;h=[x.className,m.addClass,m.removeClass].join(" ");if(!Xa(h))return A(),p;var s=0<=["enter","move","leave"].indexOf(k),t=v[0].hidden,w=!g||t||H.get(x);h=!w&&y.get(x)||{};var I=!!h.state;w|| +I&&1==h.state||(w=!K(a,oa,k));if(w)return t&&l(p,k,"start"),A(),t&&l(p,k,"close"),p;s&&L(a);t={structural:s,element:a,event:k,addClass:m.addClass,removeClass:m.removeClass,close:A,options:m,runner:p};if(I){if(d("skip",a,t,h)){if(2===h.state)return A(),p;V(a,h,t);return h.runner}if(d("cancel",a,t,h))if(2===h.state)h.runner.end();else if(h.structural)h.close();else return V(a,h,t),h.runner;else if(d("join",a,t,h))if(2===h.state)V(a,t,{});else return Sa(a,s?k:null,m),k=t.event=h.event,m=V(a,h,t),h.runner}else V(a, +t,{});(I=t.structural)||(I="animate"===t.event&&0<Object.keys(t.options.to||{}).length||e(t));if(!I)return A(),O(a),p;var ia=(h.counter||0)+1;t.counter=ia;M(a,1,t);c.$$postDigest(function(){var b=y.get(x),c=!b,b=b||{},g=0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==ia||!g){c&&(F(a,m),ga(a,m));if(c||s&&b.event!==k)m.domOperation(),p.end();g||O(a)}else k=!b.structural&&e(b,!0)?"setClass":b.event,M(a,2),b=Wa(a,k,b.options),p.setHost(b),l(p,k,"start",{}),b.done(function(b){A(!b); +(b=y.get(x))&&b.counter===ia&&O(D(a));l(p,k,"close",{})})});return p}function L(a){a=D(a).querySelectorAll("[data-ng-animate]");r(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=y.get(a);if(c)switch(b){case 2:c.runner.end();case 1:y.remove(a)}})}function O(a){a=D(a);a.removeAttribute("data-ng-animate");y.remove(a)}function l(a,b){return D(a)===D(b)}function K(a,b,c){c=G(v[0].body);var g=l(a,c)||"HTML"===a[0].nodeName,d=l(a,f),h=!1,k,e=H.get(D(a));(a=G.data(a[0],"$ngAnimatePin"))&& +(b=a);for(b=D(b);b;){d||(d=l(b,f));if(1!==b.nodeType)break;a=y.get(b)||{};if(!h){var p=H.get(b);if(!0===p&&!1!==e){e=!0;break}else!1===p&&(e=!1);h=a.structural}if(C(k)||!0===k)a=G.data(b,"$$ngAnimateChildren"),da(a)&&(k=a);if(h&&!1===k)break;g||(g=l(b,c));if(g&&d)break;if(!d&&(a=G.data(b,"$ngAnimatePin"))){b=D(a);continue}b=b.parentNode}return(!h||k)&&!0!==e&&d&&g}function M(a,b,c){c=c||{};c.state=b;a=D(a);a.setAttribute("data-ng-animate",b);c=(b=y.get(a))?Ea(b,c):c;y.put(a,c)}var y=new I,H=new I, +g=null,oa=c.$watch(function(){return 0===sa.totalPendingRequests},function(a){a&&(oa(),c.$$postDigest(function(){c.$$postDigest(function(){null===g&&(g=!0)})}))}),h={},A=a.classNameFilter(),Xa=A?function(a){return A.test(a)}:function(){return!0},F=U(w),ia=S.Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},N={on:function(a,b,c){var g=ca(b);h[a]=h[a]||[];h[a].push({node:g,callback:c});G(b).on("$destroy",function(){y.get(g)||N.off(a,b,c)})},off:function(a, +b,c){if(1!==arguments.length||q.isString(arguments[0])){var g=h[a];g&&(h[a]=1===arguments.length?null:k(g,b,c))}else for(g in b=arguments[0],h)h[g]=k(h[g],b)},pin:function(a,b){Aa(wa(a),"element","not an element");Aa(wa(b),"parentElement","not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,g){c=c||{};c.domOperation=g;return p(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!g;else if(wa(a)){var d=D(a),h=H.get(d);1===c?b=!h:H.put(d,!b)}else b=g=!!a;return b}};return N}]}]).provider("$$animation", +["$animateProvider",function(a){function b(a){return a.data("$$animationRunner")}var c=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,e,f,z,B,s){function v(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,L=d.parentNode;e.put(d,a);for(var f;L;){if(f=e.get(L)){f.processed||(f=b(f));break}L=L.parentNode}(f||c).children.push(a);return a}var c={children:[]},d,e=new B;for(d=0;d<a.length;d++){var f=a[d];e.put(f.domNode, +a[d]={domNode:f.domNode,fn:f.fn,children:[]})}for(d=0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var e=0,f=[];for(d=0;d<c.length;d++){var x=c[d];0>=a&&(a=e,e=0,b.push(f),f=[]);f.push(x.fn);x.children.forEach(function(a){e++;c.push(a)});a--}f.length&&b.push(f);return b}(c)}var I=[],q=U(a);return function(u,B,w){function x(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];r(a,function(a){var c= +a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function R(a){var b=[],c={};r(a,function(a,g){var d=D(a.element),e=0<=["enter","move"].indexOf(a.event),d=a.structural?x(d):[];if(d.length){var k=e?"to":"from";r(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][k]={animationID:g,element:G(a)}})}else b.push(a)});var d={},e={};r(c,function(c,h){var k=c.from,f=c.to;if(k&&f){var p=a[k.animationID],y=a[f.animationID],l=k.animationID.toString();if(!e[l]){var x=e[l]= +{structural:!0,beforeStart:function(){p.beforeStart();y.beforeStart()},close:function(){p.close();y.close()},classes:J(p.classes,y.classes),from:p,to:y,anchors:[]};x.classes.length?b.push(x):(b.push(p),b.push(y))}e[l].anchors.push({out:k.element,"in":f.element})}else k=k?k.animationID:f.animationID,f=k.toString(),d[f]||(d[f]=!0,b.push(a[k]))});return b}function J(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d<a.length;d++){var k=a[d];if("ng-"!==k.substring(0,3))for(var e=0;e<b.length;e++)if(k=== +b[e]){c.push(k);break}}return c.join(" ")}function k(a){for(var b=c.length-1;0<=b;b--){var d=c[b];if(f.has(d)&&(d=f.get(d)(a)))return d}}function p(a,c){a.from&&a.to?(b(a.from.element).setHost(c),b(a.to.element).setHost(c)):b(a.element).setHost(c)}function L(){var a=b(u);!a||"leave"===B&&w.$$domOperationFired||a.end()}function O(b){u.off("$destroy",L);u.removeData("$$animationRunner");q(u,w);ga(u,w);w.domOperation();y&&a.removeClass(u,y);u.removeClass("ng-animate");K.complete(!b)}w=pa(w);var l=0<= +["enter","move","leave"].indexOf(B),K=new z({end:function(){O()},cancel:function(){O(!0)}});if(!c.length)return O(),K;u.data("$$animationRunner",K);var M=Ba(u.attr("class"),Ba(w.addClass,w.removeClass)),y=w.tempClasses;y&&(M+=" "+y,w.tempClasses=null);var H;l&&(H="ng-"+B+"-prepare",a.addClass(u,H));I.push({element:u,classes:M,event:B,structural:l,options:w,beforeStart:function(){u.addClass("ng-animate");y&&a.addClass(u,y);H&&(a.removeClass(u,H),H=null)},close:O});u.on("$destroy",L);if(1<I.length)return K; +e.$$postDigest(function(){var a=[];r(I,function(c){b(c.element)?a.push(c):c.close()});I.length=0;var c=R(a),d=[];r(c,function(a){d.push({domNode:D(a.from?a.from.element:a.element),fn:function(){a.beforeStart();var c,d=a.close;if(b(a.anchors?a.from.element||a.to.element:a.element)){var g=k(a);g&&(c=g.start)}c?(c=c(),c.done(function(a){d(!a)}),p(a,c)):d()}})});s(v(d))});return K}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Ha(),c=Ha();this.$get=["$window","$$jqLite","$$AnimateRunner", +"$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,f,z,B,s,v,I){function q(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++R))+"-"+a.getAttribute("class")+"-"+b}function u(k,f,x,s){var l;0<b.count(x)&&(l=c.get(x),l||(f=X(f,"-stagger"),e.addClass(k,f),l=Fa(a,k,s),l.animationDuration=Math.max(l.animationDuration,0),l.transitionDuration=Math.max(l.transitionDuration,0),e.removeClass(k,f),c.put(x,l)));return l||{}}function sa(a){J.push(a); +v.waitUntilQuiet(function(){b.flush();c.flush();for(var a=B(),d=0;d<J.length;d++)J[d](a);J.length=0})}function w(c,e,f){e=b.get(f);e||(e=Fa(a,c,Ua),"infinite"===e.animationIterationCount&&(e.animationIterationCount=1));b.put(f,e);c=e;f=c.animationDelay;e=c.transitionDelay;c.maxDelay=f&&e?Math.max(f,e):f||e;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var x=U(e),R=0,J=[];return function(a,c){function d(){l()}function v(){l(!0)}function l(b){if(!(R|| +G&&N)){R=!0;N=!1;g.$$skipPreparationClasses||e.removeClass(a,fa);e.removeClass(a,da);ta(h,!1);qa(h,!1);r(A,function(a){h.style[a[0]]=""});x(a,g);ga(a,g);Object.keys(J).length&&r(J,function(a,b){a?h.style.setProperty(b,a):h.style.removeProperty(b)});if(g.onDone)g.onDone();ea&&ea.length&&a.off(ea.join(" "),y);var c=a.data("$$animateCss");c&&(z.cancel(c[0].timer),a.removeData("$$animateCss"));C&&C.complete(!b)}}function K(a){n.blockTransition&&qa(h,a);n.blockKeyframeAnimation&&ta(h,!!a)}function M(){C= +new f({end:d,cancel:v});sa(Q);l();return{$$willAnimate:!1,start:function(){return C},end:d}}function y(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));Math.max(a-V,0)>=S&&b>=m&&(G=!0,l())}function H(){function b(){if(!R){K(!1);r(A,function(a){h.style[a[0]]=a[1]});x(a,g);e.addClass(a,da);if(n.recalculateTimingStyles){na=h.className+" "+fa;ja=q(h,na);E=w(h,na,ja);$=E.maxDelay;ha=Math.max($,0);m=E.maxDuration;if(0===m){l();return}n.hasTransitions= +0<E.transitionDuration;n.hasAnimations=0<E.animationDuration}n.applyAnimationDelay&&($="boolean"!==typeof g.delay&&ua(g.delay)?parseFloat(g.delay):$,ha=Math.max($,0),E.animationDelay=$,aa=[ra,$+"s"],A.push(aa),h.style[aa[0]]=aa[1]);S=1E3*ha;U=1E3*m;if(g.easing){var d,f=g.easing;n.hasTransitions&&(d=T+"TimingFunction",A.push([d,f]),h.style[d]=f);n.hasAnimations&&(d=Z+"TimingFunction",A.push([d,f]),h.style[d]=f)}E.transitionDuration&&ea.push(xa);E.animationDuration&&ea.push(ya);V=Date.now();var H=S+ +1.5*U;d=V+H;var f=a.data("$$animateCss")||[],s=!0;if(f.length){var p=f[0];(s=d>p.expectedEndTime)?z.cancel(p.timer):f.push(l)}s&&(H=z(c,H,!1),f[0]={timer:H,expectedEndTime:d},f.push(l),a.data("$$animateCss",f));if(ea.length)a.on(ea.join(" "),y);g.to&&(g.cleanupStyles&&Ia(J,h,Object.keys(g.to)),Da(a,g))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!R)if(h.parentNode){var d=function(a){if(G)N&&a&&(N=!1,l());else if(N=!a,E.animationDuration)if(a= +ta(h,N),N)A.push(a);else{var b=A,c=b.indexOf(a);0<=a&&b.splice(c,1)}},f=0<ca&&(E.transitionDuration&&0===W.transitionDuration||E.animationDuration&&0===W.animationDuration)&&Math.max(W.animationDelay,W.transitionDelay);f?z(b,Math.floor(f*ca*1E3),!1):b();P.resume=function(){d(!0)};P.pause=function(){d(!1)}}else l()}var g=c||{};g.$$prepared||(g=pa(Ja(g)));var J={},h=D(a);if(!h||!h.parentNode||!I.enabled())return M();var A=[],B=a.attr("class"),F=Na(g),R,N,G,C,P,ha,S,m,U,V,ea=[];if(0===g.duration||!s.animations&& +!s.transitions)return M();var ka=g.event&&ba(g.event)?g.event.join(" "):g.event,Y="",t="";ka&&g.structural?Y=X(ka,"ng-",!0):ka&&(Y=ka);g.addClass&&(t+=X(g.addClass,"-add"));g.removeClass&&(t.length&&(t+=" "),t+=X(g.removeClass,"-remove"));g.applyClassesEarly&&t.length&&x(a,g);var fa=[Y,t].join(" ").trim(),na=B+" "+fa,da=X(fa,"-active"),B=F.to&&0<Object.keys(F.to).length;if(!(0<(g.keyframeStyle||"").length||B||fa))return M();var ja,W;0<g.stagger?(F=parseFloat(g.stagger),W={transitionDelay:F,animationDelay:F, +transitionDuration:0,animationDuration:0}):(ja=q(h,na),W=u(h,fa,ja,Va));g.$$skipPreparationClasses||e.addClass(a,fa);g.transitionStyle&&(F=[T,g.transitionStyle],la(h,F),A.push(F));0<=g.duration&&(F=0<h.style[T].length,F=Ga(g.duration,F),la(h,F),A.push(F));g.keyframeStyle&&(F=[Z,g.keyframeStyle],la(h,F),A.push(F));var ca=W?0<=g.staggerIndex?g.staggerIndex:b.count(ja):0;(ka=0===ca)&&!g.skipBlocking&&qa(h,9999);var E=w(h,na,ja),$=E.maxDelay;ha=Math.max($,0);m=E.maxDuration;var n={};n.hasTransitions= +0<E.transitionDuration;n.hasAnimations=0<E.animationDuration;n.hasTransitionAll=n.hasTransitions&&"all"==E.transitionProperty;n.applyTransitionDuration=B&&(n.hasTransitions&&!n.hasTransitionAll||n.hasAnimations&&!n.hasTransitions);n.applyAnimationDuration=g.duration&&n.hasAnimations;n.applyTransitionDelay=ua(g.delay)&&(n.applyTransitionDuration||n.hasTransitions);n.applyAnimationDelay=ua(g.delay)&&n.hasAnimations;n.recalculateTimingStyles=0<t.length;if(n.applyTransitionDuration||n.applyAnimationDuration)m= +g.duration?parseFloat(g.duration):m,n.applyTransitionDuration&&(n.hasTransitions=!0,E.transitionDuration=m,F=0<h.style[T+"Property"].length,A.push(Ga(m,F))),n.applyAnimationDuration&&(n.hasAnimations=!0,E.animationDuration=m,A.push([za,m+"s"]));if(0===m&&!n.recalculateTimingStyles)return M();if(null!=g.delay){var aa;"boolean"!==typeof g.delay&&(aa=parseFloat(g.delay),ha=Math.max(aa,0));n.applyTransitionDelay&&A.push([ma,aa+"s"]);n.applyAnimationDelay&&A.push([ra,aa+"s"])}null==g.duration&&0<E.transitionDuration&& +(n.recalculateTimingStyles=n.recalculateTimingStyles||ka);S=1E3*ha;U=1E3*m;g.skipBlocking||(n.blockTransition=0<E.transitionDuration,n.blockKeyframeAnimation=0<E.animationDuration&&0<W.animationDelay&&0===W.animationDuration);g.from&&(g.cleanupStyles&&Ia(J,h,Object.keys(g.from)),Ca(a,g));n.blockTransition||n.blockKeyframeAnimation?K(m):g.skipBlocking||qa(h,!1);return{$$willAnimate:!0,end:d,start:function(){if(!R)return P={end:d,cancel:v,resume:null,pause:null},C=new f(P),sa(H),C}}}}]}]).provider("$$animateCssDriver", +["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,c,d,e,f,z,B){function s(a){return a.replace(/\bng-\S+\b/g,"")}function v(a,b){P(a)&&(a=a.split(" "));P(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function I(c,e,f){function k(a){var b={},c=D(a).getBoundingClientRect();r(["width","height","top","left"],function(a){var d=c[a]; +switch(a){case "top":d+=C.scrollTop;break;case "left":d+=C.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function p(){var c=s(f.attr("class")||""),d=v(c,l),c=v(l,c),d=a(z,{to:k(f),addClass:"ng-anchor-in "+d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function B(){z.remove();e.removeClass("ng-animate-shim");f.removeClass("ng-animate-shim")}var z=G(D(e).cloneNode(!0)),l=s(z.attr("class")||"");e.addClass("ng-animate-shim");f.addClass("ng-animate-shim");z.addClass("ng-anchor"); +w.append(z);var K;c=function(){var c=a(z,{addClass:"ng-anchor-out",delay:!0,from:k(e)});return c.$$willAnimate?c:null}();if(!c&&(K=p(),!K))return B();var M=c||K;return{start:function(){function a(){c&&c.end()}var b,c=M.start();c.done(function(){c=null;if(!K&&(K=p()))return c=K.start(),c.done(function(){c=null;B();b.complete()}),c;B();b.complete()});return b=new d({end:a,cancel:a})}}}function q(a,b,c,e){var f=u(a,Q),s=u(b,Q),z=[];r(e,function(a){(a=I(c,a.out,a["in"]))&&z.push(a)});if(f||s||0!==z.length)return{start:function(){function a(){r(b, +function(a){a.end()})}var b=[];f&&b.push(f.start());s&&b.push(s.start());r(z,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});return c}}}function u(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=Y(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!f.animations&&!f.transitions)return Q;var C=B[0].body; +c=D(e);var w=G(c.parentNode&&11===c.parentNode.nodeType||C.contains(c)?c:C);U(z);return function(a){return a.from&&a.to?q(a.from,a.to,a.classes,a.anchors):u(a)}}]}]).provider("$$animateJs",["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ba(c)?c:c.split(" ");for(var d=[],e={},f=0;f<c.length;f++){var r=c[f],q=a.$$registeredAnimations[r];q&&!e[r]&&(d.push(b.get(q)),e[r]=!0)}return d}var f=U(d);return function(a,b,d,v){function q(){v.domOperation(); +f(a,v)}function D(a,b,d,e,g){switch(d){case "animate":b=[b,e.from,e.to,g];break;case "setClass":b=[b,x,G,g];break;case "addClass":b=[b,x,g];break;case "removeClass":b=[b,G,g];break;default:b=[b,g]}b.push(e);if(a=a.apply(a,b))if(Ka(a.start)&&(a=a.start()),a instanceof c)a.done(g);else if(Ka(a))return a;return Q}function u(a,b,d,e,g){var f=[];r(e,function(e){var k=e[g];k&&f.push(function(){var e,g,f=!1,h=function(a){f||(f=!0,(g||Q)(a),e.complete(!a))};e=new c({end:function(){h()},cancel:function(){h(!0)}}); +g=D(k,a,b,d,function(a){h(!1===a)});return e})});return f}function C(a,b,d,e,g){var f=u(a,b,d,e,g);if(0===f.length){var h,k;"beforeSetClass"===g?(h=u(a,"removeClass",d,e,"beforeRemoveClass"),k=u(a,"addClass",d,e,"beforeAddClass")):"setClass"===g&&(h=u(a,"removeClass",d,e,"removeClass"),k=u(a,"addClass",d,e,"addClass"));h&&(f=f.concat(h));k&&(f=f.concat(k))}if(0!==f.length)return function(a){var b=[];f.length&&r(f,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){r(b,function(b){a? +b.cancel():b.end()})}}}var w=!1;3===arguments.length&&va(d)&&(v=d,d=null);v=pa(v);d||(d=a.attr("class")||"",v.addClass&&(d+=" "+v.addClass),v.removeClass&&(d+=" "+v.removeClass));var x=v.addClass,G=v.removeClass,J=e(d),k,p;if(J.length){var L,O;"leave"==b?(O="leave",L="afterLeave"):(O="before"+b.charAt(0).toUpperCase()+b.substr(1),L=b);"enter"!==b&&"move"!==b&&(k=C(a,b,v,J,O));p=C(a,b,v,J,L)}if(k||p){var l;return{$$willAnimate:!0,end:function(){l?l.end():(w=!0,q(),ga(a,v),l=new c,l.complete(!0));return l}, +start:function(){function b(c){w=!0;q();ga(a,v);l.complete(c)}if(l)return l;l=new c;var d,e=[];k&&e.push(function(a){d=k(a)});e.length?e.push(function(a){q();a(!0)}):q();p&&e.push(function(a){d=p(a)});l.setHost({end:function(){w||((d||Q)(void 0),b(void 0))},cancel:function(){w||((d||Q)(!0),b(!0))}});c.chain(e,b);return l}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element, +c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),q=d(a.to);if(b||q)return{start:function(){function a(){return function(){r(d,function(a){a.end()})}}var d=[];b&&d.push(b.start());q&&d.push(q.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular); +//# sourceMappingURL=angular-animate.min.js.map diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-aria/angular-aria.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-aria/angular-aria.min.js new file mode 100644 index 0000000000000000000000000000000000000000..274cce2f9da56bfecad9f50762b2408202187053 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-aria/angular-aria.min.js @@ -0,0 +1,14 @@ +/* + AngularJS v1.5.5 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(t,p){'use strict';var b="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};p.module("ngAria",["ng"]).provider("$aria",function(){function a(a,b,m,h){return function(d,f,e){var q=e.$normalize(b);!c[q]||l(f,m)||e[q]||d.$watch(e[a],function(a){a=h?!a:!!a;f.attr(b,a)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0}; +this.config=function(a){c=p.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",b,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",b,!1)}]).directive("ngReadonly", +["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",b,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",b,!1)}]).directive("ngModel",["$aria",function(a){function c(c,h,d,f){return a.config(h)&&!d.attr(c)&&(f||!l(d,b))}function g(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function k(a,c){var d=a.type,f=a.role;return"checkbox"===(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"=== +f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(b,h){var d=k(h,b);return{pre:function(a,e,c,b){"checkbox"===d&&(b.$isEmpty=function(a){return!1===a})},post:function(f,e,b,n){function h(){return n.$modelValue}function k(a){e.attr("aria-checked",b.value==n.$viewValue)}function l(){e.attr("aria-checked",!n.$isEmpty(n.$viewValue))}var m=c("tabindex","tabindex",e,!1);switch(d){case "radio":case "checkbox":g(d,e)&&e.attr("role", +d);c("aria-checked","ariaChecked",e,!1)&&f.$watch(h,"radio"===d?k:l);m&&e.attr("tabindex",0);break;case "range":g(d,e)&&e.attr("role","slider");if(a.config("ariaValue")){var p=!e.attr("aria-valuemin")&&(b.hasOwnProperty("min")||b.hasOwnProperty("ngMin")),r=!e.attr("aria-valuemax")&&(b.hasOwnProperty("max")||b.hasOwnProperty("ngMax")),s=!e.attr("aria-valuenow");p&&b.$observe("min",function(a){e.attr("aria-valuemin",a)});r&&b.$observe("max",function(a){e.attr("aria-valuemax",a)});s&&f.$watch(h,function(a){e.attr("aria-valuenow", +a)})}m&&e.attr("tabindex",0)}!b.hasOwnProperty("ngRequired")&&n.$validators.required&&c("aria-required","ariaRequired",e,!1)&&b.$observe("required",function(){e.attr("aria-required",!!b.required)});c("aria-invalid","ariaInvalid",e,!0)&&f.$watch(function(){return n.$invalid},function(a){e.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",b,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages", +link:function(a,b,g,k){b.attr("aria-live")||b.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(g,k){var m=c(k.ngClick,null,!0);return function(c,d,f){if(!l(d,b)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!f.ngKeypress))d.on("keypress",function(a){function b(){m(c,{$event:a})}var d=a.which||a.keyCode;32!==d&& +13!==d||c.$apply(b)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,g,k){!a.config("tabindex")||g.attr("tabindex")||l(g,b)||g.attr("tabindex",0)}}])})(window,window.angular); +//# sourceMappingURL=angular-aria.min.js.map diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.css b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.css new file mode 100644 index 0000000000000000000000000000000000000000..50b66e8d51da605f5149c55e576473da6869dfbf --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.css @@ -0,0 +1,6 @@ +/*! + * Angular Material Design + * https://github.com/angular/material + * @license MIT + * v1.1.0-rc.5 + */body,html{height:100%;position:relative}body{margin:0;padding:0}[tabindex='-1']:focus{outline:0}.inset{padding:10px}a._md-no-style,button._md-no-style{font-weight:400;background-color:inherit;text-align:left;border:none;padding:0;margin:0}button,input,select,textarea{vertical-align:baseline}button,html input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default}textarea{vertical-align:top;overflow:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box;-webkit-box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input:-webkit-autofill{text-shadow:none}._md-visually-hidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;text-transform:none;width:1px}.md-shadow{position:absolute;top:0;left:0;bottom:0;right:0;border-radius:inherit;pointer-events:none}.md-shadow-bottom-z-1{box-shadow:0 2px 5px 0 rgba(0,0,0,.26)}.md-shadow-bottom-z-2{box-shadow:0 4px 8px 0 rgba(0,0,0,.4)}.md-shadow-animated.md-shadow{transition:box-shadow .28s cubic-bezier(.4,0,.2,1)}.md-ripple-container{pointer-events:none;position:absolute;overflow:hidden;left:0;top:0;width:100%;height:100%;transition:all .55s cubic-bezier(.25,.8,.25,1)}.md-ripple{position:absolute;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transform-origin:50% 50%;transform-origin:50% 50%;opacity:0;border-radius:50%}.md-ripple.md-ripple-placed{transition:margin .9s cubic-bezier(.25,.8,.25,1),border .9s cubic-bezier(.25,.8,.25,1),width .9s cubic-bezier(.25,.8,.25,1),height .9s cubic-bezier(.25,.8,.25,1),opacity .9s cubic-bezier(.25,.8,.25,1),-webkit-transform .9s cubic-bezier(.25,.8,.25,1);transition:margin .9s cubic-bezier(.25,.8,.25,1),border .9s cubic-bezier(.25,.8,.25,1),width .9s cubic-bezier(.25,.8,.25,1),height .9s cubic-bezier(.25,.8,.25,1),opacity .9s cubic-bezier(.25,.8,.25,1),transform .9s cubic-bezier(.25,.8,.25,1)}.md-ripple.md-ripple-scaled{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.md-ripple.md-ripple-active,.md-ripple.md-ripple-full,.md-ripple.md-ripple-visible{opacity:.2}.md-padding{padding:8px}.md-margin{margin:8px}.md-scroll-mask{position:absolute;background-color:transparent;top:0;right:0;bottom:0;left:0;z-index:50}.md-scroll-mask>.md-scroll-mask-bar{display:block;position:absolute;background-color:#fafafa;right:0;top:0;bottom:0;z-index:65;box-shadow:inset 0 0 1px rgba(0,0,0,.3)}@media (min-width:960px){.md-padding{padding:16px}}body[dir=ltr],body[dir=rtl],html[dir=ltr],html[dir=rtl]{unicode-bidi:embed}bdo[dir=rtl]{direction:rtl;unicode-bidi:bidi-override}bdo[dir=ltr]{direction:ltr;unicode-bidi:bidi-override}body,html{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;min-height:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.md-display-4{font-size:112px;font-weight:300;letter-spacing:-.010em;line-height:112px}.md-display-3{font-size:56px;font-weight:400;letter-spacing:-.005em;line-height:56px}.md-display-2{font-size:45px;font-weight:400;line-height:64px}.md-display-1{font-size:34px;font-weight:400;line-height:40px}.md-headline{font-size:24px;font-weight:400;line-height:32px}.md-title{font-size:20px;font-weight:500;letter-spacing:.005em}.md-subhead{font-size:16px;font-weight:400;letter-spacing:.010em;line-height:24px}.md-body-1{font-size:14px;font-weight:400;letter-spacing:.010em;line-height:20px}.md-body-2{font-size:14px;font-weight:500;letter-spacing:.010em;line-height:24px}.md-caption{font-size:12px;letter-spacing:.020em}.md-button{letter-spacing:.010em}button,html,input,select,textarea{font-family:Roboto,"Helvetica Neue",sans-serif}button,input,select,textarea{font-size:100%}.layout-column>.flex{-ms-flex-basis:auto;-webkit-flex-basis:auto;-ms-flex-preferred-size:auto;flex-basis:auto}@-webkit-keyframes md-autocomplete-list-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear}50%{opacity:0;height:40px;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{height:0;opacity:0}}@keyframes md-autocomplete-list-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear}50%{opacity:0;height:40px;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{height:0;opacity:0}}@-webkit-keyframes md-autocomplete-list-in{0%{opacity:0;height:0;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{opacity:0;height:40px}100%{opacity:1;height:40px}}@keyframes md-autocomplete-list-in{0%{opacity:0;height:0;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{opacity:0;height:40px}100%{opacity:1;height:40px}}md-autocomplete{border-radius:2px;display:block;height:40px;position:relative;overflow:visible;min-width:190px}md-autocomplete[disabled] input{cursor:default}md-autocomplete[md-floating-label]{border-radius:0;background:0 0;height:auto}md-autocomplete[md-floating-label] md-input-container{padding-bottom:0}md-autocomplete[md-floating-label] md-autocomplete-wrap{height:auto}md-autocomplete[md-floating-label] button{position:absolute;top:auto;bottom:0;right:0;width:30px;height:30px}md-autocomplete md-autocomplete-wrap{display:block;position:relative;overflow:visible;height:40px}md-autocomplete md-autocomplete-wrap.md-menu-showing{z-index:51}md-autocomplete md-autocomplete-wrap md-progress-linear{position:absolute;bottom:-2px;left:0}md-autocomplete md-autocomplete-wrap md-progress-linear.md-inline{bottom:40px;right:2px;left:2px;width:auto}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate{position:absolute;top:0;left:0;width:100%;height:3px;transition:none}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate .md-container{transition:none;height:3px}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-enter{transition:opacity .15s linear}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-enter.ng-enter-active{opacity:1}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-leave{transition:opacity .15s linear}md-autocomplete md-autocomplete-wrap md-progress-linear ._md-mode-indeterminate.ng-leave.ng-leave-active{opacity:0}md-autocomplete input:not(.md-input){font-size:14px;box-sizing:border-box;border:none;box-shadow:none;outline:0;background:0 0;width:100%;padding:0 15px;line-height:40px;height:40px}md-autocomplete input:not(.md-input)::-ms-clear{display:none}md-autocomplete button{position:relative;line-height:20px;text-align:center;width:30px;height:30px;cursor:pointer;border:none;border-radius:50%;padding:0;font-size:12px;background:0 0;margin:auto 5px}md-autocomplete button:after{content:'';position:absolute;top:-6px;right:-6px;bottom:-6px;left:-6px;border-radius:50%;-webkit-transform:scale(0);transform:scale(0);opacity:0;transition:all .4s cubic-bezier(.25,.8,.25,1)}md-autocomplete button:focus{outline:0}md-autocomplete button:focus:after{-webkit-transform:scale(1);transform:scale(1);opacity:1}md-autocomplete button md-icon{position:absolute;top:50%;left:50%;-webkit-transform:translate3d(-50%,-50%,0) scale(.9);transform:translate3d(-50%,-50%,0) scale(.9)}md-autocomplete button md-icon path{stroke-width:0}md-autocomplete button.ng-enter{-webkit-transform:scale(0);transform:scale(0);transition:-webkit-transform .15s ease-out;transition:transform .15s ease-out}md-autocomplete button.ng-enter.ng-enter-active{-webkit-transform:scale(1);transform:scale(1)}md-autocomplete button.ng-leave{transition:-webkit-transform .15s ease-out;transition:transform .15s ease-out}md-autocomplete button.ng-leave.ng-leave-active{-webkit-transform:scale(0);transform:scale(0)}@media screen and (-ms-high-contrast:active){md-autocomplete input{border:1px solid #fff}md-autocomplete li:focus{color:#fff}}.md-virtual-repeat-container.md-autocomplete-suggestions-container{position:absolute;box-shadow:0 2px 5px rgba(0,0,0,.25);height:225.5px;max-height:225.5px;z-index:100}.md-virtual-repeat-container.md-not-found{height:48px}.md-autocomplete-suggestions{margin:0;list-style:none;padding:0}.md-autocomplete-suggestions li{font-size:14px;overflow:hidden;padding:0 15px;line-height:48px;height:48px;transition:background .15s linear;margin:0;white-space:nowrap;text-overflow:ellipsis}.md-autocomplete-suggestions li:focus{outline:0}.md-autocomplete-suggestions li:not(.md-not-found-wrapper){cursor:pointer}@media screen and (-ms-high-contrast:active){.md-autocomplete-suggestions,md-autocomplete{border:1px solid #fff}}md-backdrop{transition:opacity 450ms;position:absolute;top:0;bottom:0;left:0;right:0;z-index:50}md-backdrop._md-menu-backdrop{position:fixed!important;z-index:99}md-backdrop._md-select-backdrop{z-index:81;transition-duration:0}md-backdrop._md-dialog-backdrop{z-index:79}md-backdrop._md-bottom-sheet-backdrop{z-index:69}md-backdrop._md-sidenav-backdrop{z-index:59}md-backdrop._md-click-catcher{position:absolute}md-backdrop.md-opaque{opacity:.48}md-backdrop.md-opaque.ng-enter{opacity:0}md-backdrop.md-opaque.ng-enter.md-opaque.ng-enter-active{opacity:.48}md-backdrop.md-opaque.ng-leave{opacity:.48;transition:opacity 400ms}md-backdrop.md-opaque.ng-leave.md-opaque.ng-leave-active{opacity:0}button.md-button::-moz-focus-inner{border:0}.md-button{border-radius:3px;box-sizing:border-box;color:currentColor;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;outline:0;border:0;display:inline-block;-webkit-align-items:center;-ms-flex-align:center;align-items:center;padding:0 6px;margin:6px 8px;line-height:36px;min-height:36px;background:0 0;white-space:nowrap;min-width:88px;text-align:center;text-transform:uppercase;font-weight:500;font-size:14px;font-style:inherit;font-variant:inherit;font-family:inherit;text-decoration:none;cursor:pointer;overflow:hidden;transition:box-shadow .4s cubic-bezier(.25,.8,.25,1),background-color .4s cubic-bezier(.25,.8,.25,1)}.md-button:focus{outline:0}.md-button:focus,.md-button:hover{text-decoration:none}.md-button.ng-hide,.md-button.ng-leave{transition:none}.md-button.md-cornered{border-radius:0}.md-button.md-icon{padding:0;background:0 0}.md-button.md-raised:not([disabled]){box-shadow:0 2px 5px 0 rgba(0,0,0,.26)}.md-button.md-icon-button{margin:0 6px;height:40px;min-width:0;line-height:24px;padding:8px;width:40px;border-radius:50%}.md-button.md-icon-button .md-ripple-container{border-radius:50%;background-clip:padding-box;overflow:hidden;-webkit-mask-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)}.md-button.md-fab{z-index:20;line-height:56px;min-width:0;width:56px;height:56px;vertical-align:middle;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);border-radius:50%;background-clip:padding-box;overflow:hidden;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-property:background-color,box-shadow,-webkit-transform;transition-property:background-color,box-shadow,transform}.md-button.md-fab.md-fab-bottom-right{top:auto;right:20px;bottom:20px;left:auto;position:absolute}.md-button.md-fab.md-fab-bottom-left{top:auto;right:auto;bottom:20px;left:20px;position:absolute}.md-button.md-fab.md-fab-top-right{top:20px;right:20px;bottom:auto;left:auto;position:absolute}.md-button.md-fab.md-fab-top-left{top:20px;right:auto;bottom:auto;left:20px;position:absolute}.md-button.md-fab .md-ripple-container{border-radius:50%;background-clip:padding-box;overflow:hidden;-webkit-mask-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)}.md-button.md-fab.md-mini{line-height:40px;width:40px;height:40px}.md-button.md-fab.ng-hide,.md-button.md-fab.ng-leave{transition:none}.md-button:not([disabled]).md-fab.md-focused,.md-button:not([disabled]).md-raised.md-focused{box-shadow:0 2px 5px 0 rgba(0,0,0,.26)}.md-button:not([disabled]).md-fab:active,.md-button:not([disabled]).md-raised:active{box-shadow:0 4px 8px 0 rgba(0,0,0,.4)}.md-button .md-ripple-container{border-radius:3px;background-clip:padding-box;overflow:hidden;-webkit-mask-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)}.md-button.md-icon-button md-icon,button.md-button.md-fab md-icon{display:block}._md-toast-open-top .md-button.md-fab-top-left,._md-toast-open-top .md-button.md-fab-top-right{transition:all .4s cubic-bezier(.25,.8,.25,1);-webkit-transform:translate3d(0,42px,0);transform:translate3d(0,42px,0)}._md-toast-open-top .md-button.md-fab-top-left:not([disabled]).md-focused,._md-toast-open-top .md-button.md-fab-top-left:not([disabled]):hover,._md-toast-open-top .md-button.md-fab-top-right:not([disabled]).md-focused,._md-toast-open-top .md-button.md-fab-top-right:not([disabled]):hover{-webkit-transform:translate3d(0,41px,0);transform:translate3d(0,41px,0)}._md-toast-open-bottom .md-button.md-fab-bottom-left,._md-toast-open-bottom .md-button.md-fab-bottom-right{transition:all .4s cubic-bezier(.25,.8,.25,1);-webkit-transform:translate3d(0,-42px,0);transform:translate3d(0,-42px,0)}._md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]).md-focused,._md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]):hover,._md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]).md-focused,._md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]):hover{-webkit-transform:translate3d(0,-43px,0);transform:translate3d(0,-43px,0)}.md-button-group{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;width:100%}.md-button-group>.md-button{-webkit-flex:1;-ms-flex:1;flex:1;display:block;overflow:hidden;width:0;border-width:1px 0 1px 1px;border-radius:0;text-align:center;text-overflow:ellipsis;white-space:nowrap}.md-button-group>.md-button:first-child{border-radius:2px 0 0 2px}.md-button-group>.md-button:last-child{border-right-width:1px;border-radius:0 2px 2px 0}@media screen and (-ms-high-contrast:active){.md-button.md-fab,.md-button.md-raised{border:1px solid #fff}}md-bottom-sheet{position:absolute;left:0;right:0;bottom:0;padding:8px 16px 88px;z-index:70;border-top-width:1px;border-top-style:solid;-webkit-transform:translate3d(0,80px,0);transform:translate3d(0,80px,0);transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:-webkit-transform;transition-property:transform}md-bottom-sheet.md-has-header{padding-top:0}md-bottom-sheet.ng-enter{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}md-bottom-sheet.ng-enter-active{opacity:1;display:block;-webkit-transform:translate3d(0,80px,0)!important;transform:translate3d(0,80px,0)!important}md-bottom-sheet.ng-leave-active{-webkit-transform:translate3d(0,100%,0)!important;transform:translate3d(0,100%,0)!important;transition:all .3s cubic-bezier(.55,0,.55,.2)}md-bottom-sheet .md-subheader{background-color:transparent;font-family:Roboto,"Helvetica Neue",sans-serif;line-height:56px;padding:0;white-space:nowrap}md-bottom-sheet md-inline-icon{display:inline-block;height:24px;width:24px;fill:#444}md-bottom-sheet md-list-item{display:-webkit-flex;display:-ms-flexbox;display:flex;outline:0}md-bottom-sheet md-list-item:hover{cursor:pointer}md-bottom-sheet.md-list md-list-item{padding:0;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:48px}md-bottom-sheet.md-grid{padding-left:24px;padding-right:24px;padding-top:0}md-bottom-sheet.md-grid md-list{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;transition:all .5s;-webkit-align-items:center;-ms-flex-align:center;align-items:center}md-bottom-sheet.md-grid md-list-item{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-ms-flex-align:center;align-items:center;transition:all .5s;height:96px;margin-top:8px;margin-bottom:8px}@media (max-width:960px){md-bottom-sheet.md-grid md-list-item{-webkit-flex:1 1 33.33333%;-ms-flex:1 1 33.33333%;flex:1 1 33.33333%;max-width:33.33333%}md-bottom-sheet.md-grid md-list-item:nth-of-type(3n+1){-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start}md-bottom-sheet.md-grid md-list-item:nth-of-type(3n){-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end}}@media (min-width:960px) and (max-width:1279px){md-bottom-sheet.md-grid md-list-item{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%}}@media (min-width:1280px) and (max-width:1919px){md-bottom-sheet.md-grid md-list-item{-webkit-flex:1 1 16.66667%;-ms-flex:1 1 16.66667%;flex:1 1 16.66667%;max-width:16.66667%}}@media (min-width:1920px){md-bottom-sheet.md-grid md-list-item{-webkit-flex:1 1 14.28571%;-ms-flex:1 1 14.28571%;flex:1 1 14.28571%;max-width:14.28571%}}md-bottom-sheet.md-grid md-list-item .md-list-item-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-ms-flex-align:center;align-items:center;width:48px;padding-bottom:16px}md-bottom-sheet.md-grid md-list-item .md-grid-item-content{border:1px solid transparent;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-ms-flex-align:center;align-items:center;width:80px}md-bottom-sheet.md-grid md-list-item .md-grid-text{font-weight:400;line-height:16px;font-size:13px;margin:0;white-space:nowrap;width:64px;text-align:center;text-transform:none;padding-top:8px}@media screen and (-ms-high-contrast:active){md-bottom-sheet{border:1px solid #fff}}.md-inline-form md-checkbox{margin:19px 0 18px}md-checkbox{box-sizing:border-box;display:inline-block;margin-bottom:16px;white-space:nowrap;cursor:pointer;outline:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;min-width:20px;min-height:20px;margin-left:0;margin-right:16px}[dir=rtl] md-checkbox{margin-left:16px;margin-right:0}md-checkbox:last-of-type{margin-left:0;margin-right:0}md-checkbox.md-focused:not([disabled]) ._md-container:before{left:-8px;top:-8px;right:-8px;bottom:-8px}md-checkbox.md-focused:not([disabled]):not(.md-checked) ._md-container:before{background-color:rgba(0,0,0,.12)}md-checkbox.md-align-top-left>div._md-container{top:12px}md-checkbox ._md-container{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);box-sizing:border-box;display:inline-block;width:20px;height:20px;left:0;right:auto}[dir=rtl] md-checkbox ._md-container{left:auto;right:0}md-checkbox ._md-container:before{box-sizing:border-box;background-color:transparent;border-radius:50%;content:'';position:absolute;display:block;height:auto;left:0;top:0;right:0;bottom:0;transition:all .5s;width:auto}md-checkbox ._md-container:after{box-sizing:border-box;content:'';position:absolute;top:-10px;right:-10px;bottom:-10px;left:-10px}md-checkbox ._md-container .md-ripple-container{position:absolute;display:block;width:auto;height:auto;left:-15px;top:-15px;right:-15px;bottom:-15px}md-checkbox ._md-icon{box-sizing:border-box;transition:240ms;position:absolute;top:0;left:0;width:20px;height:20px;border-width:2px;border-style:solid;border-radius:2px}md-checkbox.md-checked ._md-icon{border:none}md-checkbox.md-checked ._md-icon:after{box-sizing:border-box;-webkit-transform:rotate(45deg);transform:rotate(45deg);position:absolute;left:6.67px;top:2.22px;display:table;width:6.67px;height:13.33px;border-width:2px;border-style:solid;border-top:0;border-left:0;content:''}md-checkbox[disabled]{cursor:default}md-checkbox.md-indeterminate ._md-icon:after{box-sizing:border-box;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:table;width:12px;height:2px;border-width:2px;border-style:solid;border-top:0;border-left:0;content:''}md-checkbox ._md-label{box-sizing:border-box;position:relative;display:inline-block;vertical-align:middle;white-space:normal;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;margin-left:30px;margin-right:0}[dir=rtl] md-checkbox ._md-label{margin-left:0;margin-right:30px}.md-contact-chips .md-chips md-chip{padding:0 25px 0 0}[dir=rtl] .md-contact-chips .md-chips md-chip{padding:0 0 0 25px}.md-contact-chips .md-chips md-chip .md-contact-avatar{float:left}[dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-avatar{float:right}.md-contact-chips .md-chips md-chip .md-contact-avatar img{height:32px;border-radius:16px}.md-contact-chips .md-chips md-chip .md-contact-name{display:inline-block;height:32px;margin-left:8px}[dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-name{margin-left:auto;margin-left:initial;margin-right:8px}.md-contact-suggestion{height:56px}.md-contact-suggestion img{height:40px;border-radius:20px;margin-top:8px}.md-contact-suggestion .md-contact-name{margin-left:8px;width:120px}[dir=rtl] .md-contact-suggestion .md-contact-name{margin-left:auto;margin-left:initial;margin-right:8px}.md-contact-suggestion .md-contact-email,.md-contact-suggestion .md-contact-name{display:inline-block;overflow:hidden;text-overflow:ellipsis}.md-contact-chips-suggestions li{height:100%}.md-chips{display:block;font-family:Roboto,"Helvetica Neue",sans-serif;font-size:16px;padding:0 0 8px 3px;vertical-align:middle}.md-chips:after{content:'';display:table;clear:both}[dir=rtl] .md-chips{padding:0 3px 8px 0}.md-chips.md-readonly ._md-chip-input-container{min-height:32px}.md-chips:not(.md-readonly){cursor:text}.md-chips:not(.md-readonly) md-chip:not(.md-readonly){padding-right:22px}[dir=rtl] .md-chips:not(.md-readonly) md-chip:not(.md-readonly){padding-right:auto;padding-right:initial;padding-left:22px}.md-chips:not(.md-readonly) md-chip:not(.md-readonly) ._md-chip-content{padding-right:4px}[dir=rtl] .md-chips:not(.md-readonly) md-chip:not(.md-readonly) ._md-chip-content{padding-right:auto;padding-right:initial;padding-left:4px}.md-chips md-chip{cursor:default;border-radius:16px;display:block;height:32px;line-height:32px;margin:8px 8px 0 0;padding:0 12px;float:left;box-sizing:border-box;max-width:100%;position:relative}[dir=rtl] .md-chips md-chip{margin:8px 0 0 8px;float:right}.md-chips md-chip ._md-chip-content{display:block;float:left;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}[dir=rtl] .md-chips md-chip ._md-chip-content{float:right}.md-chips md-chip ._md-chip-content:focus{outline:0}.md-chips md-chip._md-chip-content-edit-is-enabled{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-ms-user-select:none}.md-chips md-chip ._md-chip-remove-container{position:absolute;right:0;line-height:22px}[dir=rtl] .md-chips md-chip ._md-chip-remove-container{right:0;right:auto;right:initial;left:0}.md-chips md-chip ._md-chip-remove{text-align:center;width:32px;height:32px;min-width:0;padding:0;background:0 0;border:none;box-shadow:none;margin:0;position:relative}.md-chips md-chip ._md-chip-remove md-icon{height:18px;width:18px;position:absolute;top:50%;left:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.md-chips ._md-chip-input-container{display:block;line-height:32px;margin:8px 8px 0 0;padding:0;float:left}[dir=rtl] .md-chips ._md-chip-input-container{margin:8px 0 0 8px;float:right}.md-chips ._md-chip-input-container input:not([type]),.md-chips ._md-chip-input-container input[type=url],.md-chips ._md-chip-input-container input[type=text],.md-chips ._md-chip-input-container input[type=email],.md-chips ._md-chip-input-container input[type=number],.md-chips ._md-chip-input-container input[type=tel]{border:0;height:32px;line-height:32px;padding:0}.md-chips ._md-chip-input-container input:not([type]):focus,.md-chips ._md-chip-input-container input[type=url]:focus,.md-chips ._md-chip-input-container input[type=text]:focus,.md-chips ._md-chip-input-container input[type=email]:focus,.md-chips ._md-chip-input-container input[type=number]:focus,.md-chips ._md-chip-input-container input[type=tel]:focus{outline:0}.md-chips ._md-chip-input-container md-autocomplete,.md-chips ._md-chip-input-container md-autocomplete-wrap{background:0 0}.md-chips ._md-chip-input-container md-autocomplete md-autocomplete-wrap{box-shadow:none}.md-chips ._md-chip-input-container input{border:0;height:32px;line-height:32px;padding:0}.md-chips ._md-chip-input-container input:focus{outline:0}.md-chips ._md-chip-input-container md-autocomplete,.md-chips ._md-chip-input-container md-autocomplete-wrap{height:32px}.md-chips ._md-chip-input-container md-autocomplete{box-shadow:none}.md-chips ._md-chip-input-container md-autocomplete input{position:relative}.md-chips ._md-chip-input-container:not(:first-child){margin:8px 8px 0 0}[dir=rtl] .md-chips ._md-chip-input-container:not(:first-child){margin:8px 0 0 8px}.md-chips ._md-chip-input-container input{background:0 0;border-width:0}.md-chips md-autocomplete button{display:none}@media screen and (-ms-high-contrast:active){._md-chip-input-container,md-chip{border:1px solid #fff}._md-chip-input-container md-autocomplete{border:none}}md-card{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;margin:8px;box-shadow:0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)}md-card md-card-header{padding:16px;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-card md-card-header:first-child md-card-avatar{margin-right:12px}[dir=rtl] md-card md-card-header:first-child md-card-avatar{margin-right:auto;margin-right:initial;margin-left:12px}md-card md-card-header:last-child md-card-avatar{margin-left:12px}[dir=rtl] md-card md-card-header:last-child md-card-avatar{margin-left:auto;margin-left:initial;margin-right:12px}md-card md-card-header md-card-avatar{width:40px;height:40px}md-card md-card-header md-card-avatar .md-user-avatar,md-card md-card-header md-card-avatar md-icon{border-radius:50%}md-card md-card-header md-card-avatar md-icon{padding:8px}md-card md-card-header md-card-avatar+md-card-header-text{max-height:40px}md-card md-card-header md-card-avatar+md-card-header-text .md-title{font-size:14px}md-card md-card-header md-card-header-text{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}md-card md-card-header md-card-header-text .md-subhead{font-size:14px}md-card>:not(md-card-content) img,md-card>img{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;width:100%;height:100%!important}md-card md-card-title{padding:24px 16px 16px;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-card md-card-title+md-card-content{padding-top:0}md-card md-card-title md-card-title-text{-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;display:-webkit-flex;display:-ms-flexbox;display:flex}md-card md-card-title md-card-title-text .md-subhead{padding-top:0;font-size:14px}md-card md-card-title md-card-title-text:only-child .md-subhead{padding-top:12px}md-card md-card-title md-card-title-media{margin-top:-8px}md-card md-card-title md-card-title-media .md-media-sm{height:80px;width:80px}md-card md-card-title md-card-title-media .md-media-md{height:112px;width:112px}md-card md-card-title md-card-title-media .md-media-lg{height:152px;width:152px}md-card md-card-content{display:block;padding:16px}md-card md-card-content>p:first-child{margin-top:0}md-card md-card-content>p:last-child{margin-bottom:0}md-card md-card-content .md-media-xl{height:240px;width:240px}md-card .md-actions,md-card md-card-actions{margin:8px}md-card .md-actions.layout-column .md-button:not(.md-icon-button),md-card md-card-actions.layout-column .md-button:not(.md-icon-button){margin:2px 0}md-card .md-actions.layout-column .md-button:not(.md-icon-button):first-of-type,md-card md-card-actions.layout-column .md-button:not(.md-icon-button):first-of-type{margin-top:0}md-card .md-actions.layout-column .md-button:not(.md-icon-button):last-of-type,md-card md-card-actions.layout-column .md-button:not(.md-icon-button):last-of-type{margin-bottom:0}md-card .md-actions.layout-column .md-button.md-icon-button,md-card md-card-actions.layout-column .md-button.md-icon-button{margin-top:6px;margin-bottom:6px}md-card .md-actions md-card-icon-actions,md-card md-card-actions md-card-icon-actions{-webkit-flex:1;-ms-flex:1;flex:1;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button),md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button){margin:0 4px}md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type,md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type{margin-left:0}[dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type,[dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type{margin-left:auto;margin-left:initial;margin-right:0}md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type,md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type{margin-right:0}[dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type,[dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type{margin-right:auto;margin-right:initial;margin-left:0}md-card .md-actions:not(.layout-column) .md-button.md-icon-button,md-card md-card-actions:not(.layout-column) .md-button.md-icon-button{margin-left:6px;margin-right:6px}md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type,md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type{margin-left:12px}[dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type,[dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type{margin-left:auto;margin-left:initial;margin-right:12px}md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type,md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type{margin-right:12px}[dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type,[dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type{margin-right:auto;margin-right:initial;margin-left:12px}md-card .md-actions:not(.layout-column) .md-button+md-card-icon-actions,md-card md-card-actions:not(.layout-column) .md-button+md-card-icon-actions{-webkit-flex:1;-ms-flex:1;flex:1;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-card md-card-footer{margin-top:auto;padding:16px}@media screen and (-ms-high-contrast:active){md-card{border:1px solid #fff}}md-content{display:block;position:relative;overflow:auto;-webkit-overflow-scrolling:touch}md-content[md-scroll-y]{overflow-y:auto;overflow-x:hidden}md-content[md-scroll-x]{overflow-x:auto;overflow-y:hidden}md-content.md-no-momentum{-webkit-overflow-scrolling:auto}@media print{md-content{overflow:visible!important}}md-calendar{font-size:13px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-calendar-scroll-mask{display:inline-block;overflow:hidden;height:308px}.md-calendar-scroll-mask .md-virtual-repeat-scroller{overflow-y:scroll;-webkit-overflow-scrolling:touch}.md-calendar-scroll-mask .md-virtual-repeat-scroller::-webkit-scrollbar{display:none}.md-calendar-scroll-mask .md-virtual-repeat-offsetter{width:100%}.md-calendar-scroll-container{box-shadow:inset -3px 3px 6px rgba(0,0,0,.2);display:inline-block;height:308px;width:346px}.md-calendar-date{height:44px;width:44px;text-align:center;padding:0;border:none;box-sizing:content-box}.md-calendar-date:first-child{padding-left:16px}[dir=rtl] .md-calendar-date:first-child{padding-left:auto;padding-left:initial;padding-right:16px}.md-calendar-date:last-child{padding-right:16px}[dir=rtl] .md-calendar-date:last-child{padding-right:auto;padding-right:initial;padding-left:16px}.md-calendar-date.md-calendar-date-disabled{cursor:default}.md-calendar-date-selection-indicator{transition:background-color,color .4s cubic-bezier(.25,.8,.25,1);border-radius:50%;display:inline-block;width:40px;height:40px;line-height:40px}.md-calendar-date:not(.md-disabled) .md-calendar-date-selection-indicator{cursor:pointer}.md-calendar-month-label{height:44px;font-size:14px;font-weight:500;padding:0 0 0 24px}[dir=rtl] .md-calendar-month-label{padding:0 24px 0 0}md-calendar-month .md-calendar-month-label:not(.md-calendar-month-label-disabled){cursor:pointer}.md-calendar-day-header{table-layout:fixed;border-spacing:0;border-collapse:collapse}.md-calendar-day-header th{width:44px;text-align:center;padding:0;border:none;box-sizing:content-box;font-weight:400;height:40px}.md-calendar-day-header th:first-child{padding-left:16px}[dir=rtl] .md-calendar-day-header th:first-child{padding-left:auto;padding-left:initial;padding-right:16px}.md-calendar-day-header th:last-child{padding-right:16px}[dir=rtl] .md-calendar-day-header th:last-child{padding-right:auto;padding-right:initial;padding-left:16px}.md-calendar{table-layout:fixed;border-spacing:0;border-collapse:collapse}.md-calendar tr:last-child td{border-bottom-width:1px;border-bottom-style:solid}.md-calendar:first-child{border-top:1px solid transparent}.md-calendar tbody,.md-calendar td,.md-calendar tr{vertical-align:middle;box-sizing:content-box}md-datepicker{white-space:nowrap;overflow:hidden;padding-right:18px;margin-right:-18px;vertical-align:middle}[dir=rtl] md-datepicker{padding-right:auto;padding-right:initial;padding-left:18px;margin-right:auto;margin-right:initial;margin-left:-18px}.md-inline-form md-datepicker{margin-top:12px}.md-datepicker-button{display:inline-block;box-sizing:border-box;background:0 0}.md-datepicker-input{font-size:14px;box-sizing:border-box;border:none;box-shadow:none;outline:0;background:0 0;min-width:120px;max-width:328px}.md-datepicker-input::-ms-clear{display:none}.md-datepicker-input-container{position:relative;padding-bottom:5px;border-bottom-width:1px;border-bottom-style:solid;display:inline-block;width:auto;margin-left:12px}[dir=rtl] .md-datepicker-input-container{margin-left:auto;margin-left:initial;margin-right:12px}.md-datepicker-input-container.md-datepicker-focused{border-bottom-width:2px}.md-datepicker-is-showing .md-scroll-mask{z-index:99}.md-datepicker-calendar-pane{position:absolute;top:0;left:0;z-index:100;border-width:1px;border-style:solid;background:0 0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:0 0;transform-origin:0 0;transition:-webkit-transform .2s cubic-bezier(.25,.8,.25,1);transition:transform .2s cubic-bezier(.25,.8,.25,1)}.md-datepicker-calendar-pane.md-pane-open{-webkit-transform:scale(1);transform:scale(1)}.md-datepicker-input-mask{height:40px;width:340px;position:relative;background:0 0;pointer-events:none;cursor:text}.md-datepicker-input-mask-opaque{position:absolute;right:0;left:120px;height:100%;margin-left:-1px}.md-datepicker-calendar{opacity:0;transition:opacity .2s cubic-bezier(.5,0,.25,1)}.md-pane-open .md-datepicker-calendar{opacity:1}.md-datepicker-calendar md-calendar:focus{outline:0}.md-datepicker-expand-triangle{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid}.md-datepicker-triangle-button{position:absolute;right:0;top:0;-webkit-transform:translateY(-25%) translateX(45%);transform:translateY(-25%) translateX(45%)}[dir=rtl] .md-datepicker-triangle-button{right:0;right:auto;right:initial;left:0;-webkit-transform:translateY(-25%) translateX(-45%);transform:translateY(-25%) translateX(-45%)}.md-datepicker-triangle-button.md-button.md-icon-button{height:100%;width:36px;position:absolute}md-datepicker[disabled] .md-datepicker-input-container{border-bottom-color:transparent}md-datepicker[disabled] .md-datepicker-triangle-button{display:none}.md-datepicker-open .md-datepicker-input-container{margin-left:-12px;margin-bottom:-5px;border:none}[dir=rtl] .md-datepicker-open .md-datepicker-input-container{margin-left:auto;margin-left:initial;margin-right:-12px}.md-datepicker-open .md-datepicker-input{margin-left:24px;height:40px}[dir=rtl] .md-datepicker-open .md-datepicker-input{margin-left:auto;margin-left:initial;margin-right:24px}.md-datepicker-open .md-datepicker-triangle-button,.md-datepicker-pos-adjusted .md-datepicker-input-mask{display:none}.md-datepicker-calendar-pane .md-calendar{-webkit-transform:translateY(-85px);transform:translateY(-85px);transition:-webkit-transform .65s cubic-bezier(.25,.8,.25,1);transition:transform .65s cubic-bezier(.25,.8,.25,1);transition-delay:.125s}.md-datepicker-calendar-pane.md-pane-open .md-calendar{-webkit-transform:translateY(0);transform:translateY(0)}.md-dialog-is-showing{max-height:100%}.md-dialog-container{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;top:0;left:0;width:100%;height:100%;z-index:80;overflow:hidden}md-dialog{opacity:0;min-width:240px;max-width:80%;max-height:80%;position:relative;overflow:auto;box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12);display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}md-dialog._md-transition-in{opacity:1;transition:all .4s cubic-bezier(.25,.8,.25,1);-webkit-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}md-dialog._md-transition-out{opacity:0;transition:all .4s cubic-bezier(.25,.8,.25,1);-webkit-transform:translate(0,100%) scale(.2);transform:translate(0,100%) scale(.2)}md-dialog>form{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow:auto}md-dialog .md-dialog-content{padding:24px}md-dialog md-dialog-content{-webkit-order:1;-ms-flex-order:1;order:1;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;overflow:auto;-webkit-overflow-scrolling:touch}md-dialog md-dialog-content:not([layout=row])>:first-child:not(.md-subheader){margin-top:0}md-dialog md-dialog-content:focus{outline:0}md-dialog md-dialog-content .md-subheader{margin:0}md-dialog md-dialog-content ._md-dialog-content-body{width:100%}md-dialog md-dialog-content .md-prompt-input-container{width:100%;box-sizing:border-box}md-dialog .md-actions,md-dialog md-dialog-actions{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-order:2;-ms-flex-order:2;order:2;box-sizing:border-box;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;margin-bottom:0;padding-right:8px;padding-left:16px;min-height:52px;overflow:hidden}[dir=rtl] md-dialog .md-actions,[dir=rtl] md-dialog md-dialog-actions{padding-right:16px;padding-left:8px}md-dialog .md-actions .md-button,md-dialog md-dialog-actions .md-button{margin:8px 0 8px 8px}[dir=rtl] md-dialog .md-actions .md-button,[dir=rtl] md-dialog md-dialog-actions .md-button{margin-left:0;margin-right:8px}md-dialog.md-content-overflow .md-actions,md-dialog.md-content-overflow md-dialog-actions{border-top-width:1px;border-top-style:solid}@media screen and (-ms-high-contrast:active){md-dialog{border:1px solid #fff}}@media (max-width:959px){md-dialog.md-dialog-fullscreen{min-height:100%;min-width:100%;border-radius:0}}md-divider{display:block;border-top-width:1px;border-top-style:solid;margin:0}md-divider[md-inset]{margin-left:80px}[dir=rtl] md-divider[md-inset]{margin-left:auto;margin-left:initial;margin-right:80px}.layout-gt-lg-row>md-divider,.layout-gt-md-row>md-divider,.layout-gt-sm-row>md-divider,.layout-gt-xs-row>md-divider,.layout-lg-row>md-divider,.layout-md-row>md-divider,.layout-row>md-divider,.layout-sm-row>md-divider,.layout-xl-row>md-divider,.layout-xs-row>md-divider{border-top-width:0;border-right-width:1px;border-right-style:solid}md-fab-speed-dial{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;z-index:20}md-fab-speed-dial.md-fab-bottom-right{top:auto;right:20px;bottom:20px;left:auto;position:absolute}md-fab-speed-dial.md-fab-bottom-left{top:auto;right:auto;bottom:20px;left:20px;position:absolute}md-fab-speed-dial.md-fab-top-right{top:20px;right:20px;bottom:auto;left:auto;position:absolute}md-fab-speed-dial.md-fab-top-left{top:20px;right:auto;bottom:auto;left:20px;position:absolute}md-fab-speed-dial:not(.md-hover-full){pointer-events:none}md-fab-speed-dial:not(.md-hover-full) .md-fab-action-item,md-fab-speed-dial:not(.md-hover-full) md-fab-trigger,md-fab-speed-dial:not(.md-hover-full).md-is-open{pointer-events:auto}md-fab-speed-dial ._md-css-variables{z-index:20}md-fab-speed-dial.md-is-open .md-fab-action-item{-webkit-align-items:center;-ms-flex-align:center;align-items:center}md-fab-speed-dial md-fab-actions{display:-webkit-flex;display:-ms-flexbox;display:flex;height:auto}md-fab-speed-dial md-fab-actions .md-fab-action-item{transition:all .3s cubic-bezier(.55,0,.55,.2)}md-fab-speed-dial.md-down{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}md-fab-speed-dial.md-down md-fab-trigger{-webkit-order:1;-ms-flex-order:1;order:1}md-fab-speed-dial.md-down md-fab-actions{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-order:2;-ms-flex-order:2;order:2}md-fab-speed-dial.md-up{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}md-fab-speed-dial.md-up md-fab-trigger{-webkit-order:2;-ms-flex-order:2;order:2}md-fab-speed-dial.md-up md-fab-actions{-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse;-webkit-order:1;-ms-flex-order:1;order:1}md-fab-speed-dial.md-left{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-fab-speed-dial.md-left md-fab-trigger{-webkit-order:2;-ms-flex-order:2;order:2}md-fab-speed-dial.md-left md-fab-actions{-webkit-flex-direction:row-reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-webkit-order:1;-ms-flex-order:1;order:1}md-fab-speed-dial.md-left md-fab-actions .md-fab-action-item{transition:all .3s cubic-bezier(.55,0,.55,.2)}md-fab-speed-dial.md-right{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-fab-speed-dial.md-right md-fab-trigger{-webkit-order:1;-ms-flex-order:1;order:1}md-fab-speed-dial.md-right md-fab-actions{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-order:2;-ms-flex-order:2;order:2}md-fab-speed-dial.md-right md-fab-actions .md-fab-action-item{transition:all .3s cubic-bezier(.55,0,.55,.2)}md-fab-speed-dial.md-fling-remove .md-fab-action-item>*,md-fab-speed-dial.md-scale-remove .md-fab-action-item>*{visibility:hidden}md-fab-speed-dial.md-fling .md-fab-action-item{opacity:1}md-fab-speed-dial.md-fling._md-animations-waiting .md-fab-action-item{opacity:0;transition-duration:0s}md-fab-speed-dial.md-scale .md-fab-action-item{-webkit-transform:scale(0);transform:scale(0);transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:.14286s}md-fab-toolbar{display:block}md-fab-toolbar.md-fab-bottom-right{top:auto;right:20px;bottom:20px;left:auto;position:absolute}md-fab-toolbar.md-fab-bottom-left{top:auto;right:auto;bottom:20px;left:20px;position:absolute}md-fab-toolbar.md-fab-top-right{top:20px;right:20px;bottom:auto;left:auto;position:absolute}md-fab-toolbar.md-fab-top-left{top:20px;right:auto;bottom:auto;left:20px;position:absolute}md-fab-toolbar ._md-fab-toolbar-wrapper{display:block;position:relative;overflow:hidden;height:68px}md-fab-toolbar md-fab-trigger{position:absolute;z-index:20}md-fab-toolbar md-fab-trigger button{overflow:visible!important}md-fab-toolbar md-fab-trigger ._md-fab-toolbar-background{display:block;position:absolute;z-index:21;opacity:1;transition:all .3s cubic-bezier(.55,0,.55,.2)}md-fab-toolbar md-fab-trigger md-icon{position:relative;z-index:22;opacity:1;transition:all 200ms ease-in}md-fab-toolbar.md-left md-fab-trigger{right:0}[dir=rtl] md-fab-toolbar.md-left md-fab-trigger{right:0;right:auto;right:initial;left:0}md-fab-toolbar.md-left .md-toolbar-tools{-webkit-flex-direction:row-reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}md-fab-toolbar.md-left .md-toolbar-tools>.md-button:first-child{margin-right:.6rem}md-fab-toolbar.md-left .md-toolbar-tools>.md-button:first-child{margin-left:-.8rem}[dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools>.md-button:first-child{margin-left:auto;margin-left:initial;margin-right:-.8rem}md-fab-toolbar.md-left .md-toolbar-tools>.md-button:last-child{margin-right:8px}[dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools>.md-button:last-child{margin-right:auto;margin-right:initial;margin-left:8px}md-fab-toolbar.md-right md-fab-trigger{left:0}[dir=rtl] md-fab-toolbar.md-right md-fab-trigger{left:0;left:auto;left:initial;right:0}md-fab-toolbar.md-right .md-toolbar-tools{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-fab-toolbar md-toolbar{background-color:transparent!important;pointer-events:none;z-index:23}md-fab-toolbar md-toolbar .md-toolbar-tools{padding:0 20px;margin-top:3px}md-fab-toolbar md-toolbar .md-fab-action-item{opacity:0;-webkit-transform:scale(0);transform:scale(0);transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:.15s}md-fab-toolbar.md-is-open md-fab-trigger>button{box-shadow:none}md-fab-toolbar.md-is-open md-fab-trigger>button md-icon{opacity:0}md-fab-toolbar.md-is-open .md-fab-action-item{opacity:1;-webkit-transform:scale(1);transform:scale(1)}md-icon{margin:auto;background-repeat:no-repeat no-repeat;display:inline-block;vertical-align:middle;fill:currentColor;height:24px;width:24px;min-height:24px;min-width:24px}md-icon svg{pointer-events:none;display:block}md-icon[md-font-icon]{line-height:24px;width:auto}md-grid-list{box-sizing:border-box;display:block;position:relative}md-grid-list md-grid-tile,md-grid-list md-grid-tile-footer,md-grid-list md-grid-tile-header,md-grid-list md-grid-tile>figure{box-sizing:border-box}md-grid-list md-grid-tile{display:block;position:absolute}md-grid-list md-grid-tile figure{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;height:100%;position:absolute;top:0;right:0;bottom:0;left:0;padding:0;margin:0}md-grid-list md-grid-tile md-grid-tile-footer,md-grid-list md-grid-tile md-grid-tile-header{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.18);overflow:hidden;position:absolute;left:0;right:0}md-grid-list md-grid-tile md-grid-tile-footer h3,md-grid-list md-grid-tile md-grid-tile-footer h4,md-grid-list md-grid-tile md-grid-tile-header h3,md-grid-list md-grid-tile md-grid-tile-header h4{font-weight:400;margin:0 0 0 16px}md-grid-list md-grid-tile md-grid-tile-footer h3,md-grid-list md-grid-tile md-grid-tile-header h3{font-size:14px}md-grid-list md-grid-tile md-grid-tile-footer h4,md-grid-list md-grid-tile md-grid-tile-header h4{font-size:12px}md-grid-list md-grid-tile md-grid-tile-header{top:0}md-grid-list md-grid-tile md-grid-tile-footer{bottom:0}@media screen and (-ms-high-contrast:active){md-grid-tile{border:1px solid #fff}md-grid-tile-footer{border-top:1px solid #fff}}md-input-container{display:inline-block;position:relative;padding:2px;margin:18px 0;vertical-align:middle}md-input-container:after{content:'';display:table;clear:both}md-input-container.md-block{display:block}md-input-container .md-errors-spacer{float:right;min-height:24px;min-width:1px}[dir=rtl] md-input-container .md-errors-spacer{float:left}md-input-container .md-resize-handle{position:absolute;bottom:22px;left:0;height:10px;background:0 0;width:100%;cursor:ns-resize}md-input-container>md-icon{position:absolute;top:8px;left:2px;right:auto}[dir=rtl] md-input-container>md-icon{left:auto;right:2px}md-input-container input[type=url],md-input-container input[type=text],md-input-container input[type=password],md-input-container input[type=datetime],md-input-container input[type=datetime-local],md-input-container input[type=date],md-input-container input[type=month],md-input-container input[type=time],md-input-container input[type=week],md-input-container input[type=color],md-input-container input[type=search],md-input-container input[type=email],md-input-container input[type=number],md-input-container input[type=tel],md-input-container textarea{-moz-appearance:none;-webkit-appearance:none}md-input-container input[type=datetime-local],md-input-container input[type=date],md-input-container input[type=month],md-input-container input[type=time],md-input-container input[type=week]{min-height:26px}md-input-container textarea{resize:none;overflow:hidden}md-input-container textarea.md-input{min-height:26px;-ms-flex-preferred-size:auto}md-input-container textarea[md-no-autogrow]{height:auto;overflow:auto}md-input-container label:not(._md-container-ignore){position:absolute;bottom:100%;left:0;right:auto}[dir=rtl] md-input-container label:not(._md-container-ignore){left:auto;right:0}md-input-container label:not(._md-container-ignore).md-required:after{content:' *';font-size:13px;vertical-align:top}md-input-container ._md-placeholder,md-input-container label:not(.md-no-float):not(._md-container-ignore){overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%;-webkit-order:1;-ms-flex-order:1;order:1;pointer-events:none;-webkit-font-smoothing:antialiased;padding-left:3px;padding-right:0;z-index:1;-webkit-transform:translate3d(0,28px,0) scale(1);transform:translate3d(0,28px,0) scale(1);transition:-webkit-transform .4s cubic-bezier(.25,.8,.25,1);transition:transform .4s cubic-bezier(.25,.8,.25,1);max-width:100%;-webkit-transform-origin:left top;transform-origin:left top}[dir=rtl] md-input-container ._md-placeholder,[dir=rtl] md-input-container label:not(.md-no-float):not(._md-container-ignore){padding-left:0;padding-right:3px;-webkit-transform-origin:right top;transform-origin:right top}md-input-container ._md-placeholder{position:absolute;top:0;opacity:0;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;-webkit-transform:translate3d(0,30px,0);transform:translate3d(0,30px,0)}md-input-container.md-input-focused ._md-placeholder{opacity:1;-webkit-transform:translate3d(0,24px,0);transform:translate3d(0,24px,0)}md-input-container.md-input-has-value ._md-placeholder{transition:none;opacity:0}md-input-container:not(.md-input-has-value) input:not(:focus),md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-ampm-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-day-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-hour-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-millisecond-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-minute-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-month-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-second-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-text,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-week-field,md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-year-field{color:transparent}md-input-container .md-input{-webkit-order:2;-ms-flex-order:2;order:2;display:block;margin-top:0;background:0 0;padding:2px 2px 1px;border-width:0 0 1px;line-height:26px;height:30px;-ms-flex-preferred-size:26px;border-radius:0;border-style:solid;width:100%;box-sizing:border-box;float:left}[dir=rtl] md-input-container .md-input{float:right}md-input-container .md-input:focus{outline:0}md-input-container .md-input:invalid{outline:0;box-shadow:none}md-input-container .md-input.md-no-flex{-webkit-flex:none!important;-ms-flex:none!important;flex:none!important}md-input-container .md-char-counter{text-align:right;padding-right:2px;padding-left:0}[dir=rtl] md-input-container .md-char-counter{text-align:left;padding-right:0;padding-left:2px}md-input-container .md-input-messages-animation{position:relative;-webkit-order:4;-ms-flex-order:4;order:4;overflow:hidden;clear:left}[dir=rtl] md-input-container .md-input-messages-animation{clear:right}md-input-container .md-input-messages-animation.ng-enter .md-input-message-animation{opacity:0;margin-top:-100px}md-input-container .md-char-counter,md-input-container .md-input-message-animation{font-size:12px;line-height:14px;overflow:hidden;transition:all .3s cubic-bezier(.55,0,.55,.2);opacity:1;margin-top:0;padding-top:5px}md-input-container .md-char-counter:not(.md-char-counter),md-input-container .md-input-message-animation:not(.md-char-counter){padding-right:5px;padding-left:0}[dir=rtl] md-input-container .md-char-counter:not(.md-char-counter),[dir=rtl] md-input-container .md-input-message-animation:not(.md-char-counter){padding-right:0;padding-left:5px}md-input-container .md-auto-hide .md-input-message-animation:not(.ng-animate),md-input-container .md-input-message-animation.ng-enter,md-input-container:not(.md-input-invalid) .md-auto-hide .md-input-message-animation{opacity:0;margin-top:-100px}md-input-container.md-input-focused label:not(.md-no-float),md-input-container.md-input-has-placeholder label:not(.md-no-float),md-input-container.md-input-has-value label:not(.md-no-float){-webkit-transform:translate3d(0,6px,0) scale(.75);transform:translate3d(0,6px,0) scale(.75);transition:-webkit-transform cubic-bezier(.25,.8,.25,1) .4s,width cubic-bezier(.25,.8,.25,1) .4s;transition:transform cubic-bezier(.25,.8,.25,1) .4s,width cubic-bezier(.25,.8,.25,1) .4s}md-input-container.md-input-has-value label{transition:none}md-input-container .md-input.ng-invalid.ng-dirty,md-input-container.md-input-focused .md-input,md-input-container.md-input-resized .md-input{padding-bottom:0;border-width:0 0 2px}[disabled] md-input-container .md-input,md-input-container .md-input[disabled]{background-position:bottom -1px left 0;background-size:4px 1px;background-repeat:repeat-x}md-input-container.md-icon-float{transition:margin-top .4s cubic-bezier(.25,.8,.25,1)}md-input-container.md-icon-float>label{pointer-events:none;position:absolute}md-input-container.md-icon-float>md-icon{top:8px;left:2px;right:auto}[dir=rtl] md-input-container.md-icon-float>md-icon{left:auto;right:2px}md-input-container.md-icon-left>label .md-placeholder,md-input-container.md-icon-left>label:not(.md-no-float):not(._md-container-ignore),md-input-container.md-icon-right>label .md-placeholder,md-input-container.md-icon-right>label:not(.md-no-float):not(._md-container-ignore){width:calc(100% - 36px - 18px)}md-input-container.md-icon-left{padding-left:36px;padding-right:0}[dir=rtl] md-input-container.md-icon-left{padding-left:0;padding-right:36px}md-input-container.md-icon-left>label{left:36px;right:auto}[dir=rtl] md-input-container.md-icon-left>label{left:auto;right:36px}md-input-container.md-icon-right{padding-left:0;padding-right:36px}[dir=rtl] md-input-container.md-icon-right{padding-left:36px;padding-right:0}md-input-container.md-icon-right>md-icon:last-of-type{margin:0;right:2px;left:auto}[dir=rtl] md-input-container.md-icon-right>md-icon:last-of-type{right:auto;left:2px}md-input-container.md-icon-left.md-icon-right{padding-left:36px;padding-right:36px}md-input-container.md-icon-left.md-icon-right>label .md-placeholder,md-input-container.md-icon-left.md-icon-right>label:not(.md-no-float):not(._md-container-ignore){width:calc(100% - (36px * 2))}@media screen and (-ms-high-contrast:active){md-input-container.md-default-theme>md-icon{fill:#fff}}md-list{display:block;padding:8px 0}md-list .md-subheader{font-size:14px;font-weight:500;letter-spacing:.010em;line-height:1.2em}md-list.md-dense md-list-item,md-list.md-dense md-list-item ._md-list-item-inner{min-height:48px}md-list.md-dense md-list-item ._md-list-item-inner md-icon:first-child,md-list.md-dense md-list-item md-icon:first-child{width:20px;height:20px}md-list.md-dense md-list-item ._md-list-item-inner>md-icon:first-child:not(.md-avatar-icon),md-list.md-dense md-list-item>md-icon:first-child:not(.md-avatar-icon){margin-right:36px}[dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner>md-icon:first-child:not(.md-avatar-icon),[dir=rtl] md-list.md-dense md-list-item>md-icon:first-child:not(.md-avatar-icon){margin-right:auto;margin-right:initial;margin-left:36px}md-list.md-dense md-list-item ._md-list-item-inner .md-avatar,md-list.md-dense md-list-item ._md-list-item-inner .md-avatar-icon,md-list.md-dense md-list-item .md-avatar,md-list.md-dense md-list-item .md-avatar-icon{margin-right:20px}[dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner .md-avatar,[dir=rtl] md-list.md-dense md-list-item ._md-list-item-inner .md-avatar-icon,[dir=rtl] md-list.md-dense md-list-item .md-avatar,[dir=rtl] md-list.md-dense md-list-item .md-avatar-icon{margin-right:auto;margin-right:initial;margin-left:20px}md-list.md-dense md-list-item ._md-list-item-inner .md-avatar,md-list.md-dense md-list-item .md-avatar{-webkit-flex:none;-ms-flex:none;flex:none;width:36px;height:36px}md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text.md-offset,md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset,md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text.md-offset{margin-left:56px}[dir=rtl] md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset,[dir=rtl] md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text.md-offset,[dir=rtl] md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset,[dir=rtl] md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text.md-offset{margin-left:auto;margin-left:initial;margin-right:56px}md-list.md-dense md-list-item.md-2-line .md-list-item-text h3,md-list.md-dense md-list-item.md-2-line .md-list-item-text h4,md-list.md-dense md-list-item.md-2-line .md-list-item-text p,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text h3,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text h4,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text p,md-list.md-dense md-list-item.md-3-line .md-list-item-text h3,md-list.md-dense md-list-item.md-3-line .md-list-item-text h4,md-list.md-dense md-list-item.md-3-line .md-list-item-text p,md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text h3,md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text h4,md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text p{line-height:1.05;font-size:12px}md-list.md-dense md-list-item.md-2-line .md-list-item-text h3,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-list-item-text h3,md-list.md-dense md-list-item.md-3-line .md-list-item-text h3,md-list.md-dense md-list-item.md-3-line>._md-no-style .md-list-item-text h3{font-size:13px}md-list.md-dense md-list-item.md-2-line,md-list.md-dense md-list-item.md-2-line>._md-no-style{min-height:60px}md-list.md-dense md-list-item.md-2-line div.md-button:first-child::before,md-list.md-dense md-list-item.md-2-line>._md-no-style div.md-button:first-child::before{content:'';min-height:60px;visibility:hidden;display:inline-block}md-list.md-dense md-list-item.md-2-line .md-avatar-icon,md-list.md-dense md-list-item.md-2-line>._md-no-style .md-avatar-icon,md-list.md-dense md-list-item.md-2-line>._md-no-style>.md-avatar,md-list.md-dense md-list-item.md-2-line>.md-avatar{margin-top:12px}md-list.md-dense md-list-item.md-3-line,md-list.md-dense md-list-item.md-3-line>._md-no-style{min-height:76px}md-list.md-dense md-list-item.md-3-line div.md-button:first-child::before,md-list.md-dense md-list-item.md-3-line>._md-no-style div.md-button:first-child::before{content:'';min-height:76px;visibility:hidden;display:inline-block}md-list.md-dense md-list-item.md-3-line>._md-no-style>.md-avatar,md-list.md-dense md-list-item.md-3-line>._md-no-style>md-icon:first-child,md-list.md-dense md-list-item.md-3-line>.md-avatar,md-list.md-dense md-list-item.md-3-line>md-icon:first-child{margin-top:16px}md-list-item{position:relative}md-list-item._md-proxy-focus.md-focused ._md-no-style{transition:background-color .15s linear}md-list-item._md-button-wrap{position:relative}md-list-item._md-button-wrap>div.md-button:first-child{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;padding:0 16px;margin:0;background-color:initial;font-weight:400;text-align:left;border:none}[dir=rtl] md-list-item._md-button-wrap>div.md-button:first-child{text-align:right}md-list-item._md-button-wrap>div.md-button:first-child>.md-button:first-child{position:absolute;top:0;left:0;height:100%;margin:0;padding:0}md-list-item._md-button-wrap>div.md-button:first-child ._md-list-item-inner{width:100%;height:100%}md-list-item ._md-no-style,md-list-item._md-no-proxy{position:relative;padding:0 16px;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}md-list-item ._md-no-style.md-button,md-list-item._md-no-proxy.md-button{font-size:inherit;height:inherit;text-align:left;text-transform:none;width:100%;white-space:normal;-webkit-flex-direction:inherit;-ms-flex-direction:inherit;flex-direction:inherit;-webkit-align-items:inherit;-ms-flex-align:inherit;align-items:inherit;border-radius:0;margin:0}[dir=rtl] md-list-item ._md-no-style.md-button,[dir=rtl] md-list-item._md-no-proxy.md-button{text-align:right}md-list-item ._md-no-style.md-button>.md-ripple-container,md-list-item._md-no-proxy.md-button>.md-ripple-container{border-radius:0}md-list-item ._md-no-style:focus,md-list-item._md-no-proxy:focus{outline:0}md-list-item.md-clickable:hover{cursor:pointer}md-list-item md-divider{position:absolute;bottom:0;left:0;width:100%}[dir=rtl] md-list-item md-divider{left:0;left:auto;left:initial;right:0}md-list-item md-divider[md-inset]{left:72px;width:calc(100% - 72px);margin:0!important}[dir=rtl] md-list-item md-divider[md-inset]{left:0;left:auto;left:initial;right:72px}md-list-item,md-list-item ._md-list-item-inner{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-items:center;-ms-flex-align:center;align-items:center;min-height:48px;height:auto}md-list-item ._md-list-item-inner>div.md-primary>md-icon:not(.md-avatar-icon),md-list-item ._md-list-item-inner>div.md-secondary>md-icon:not(.md-avatar-icon),md-list-item ._md-list-item-inner>md-icon.md-secondary:not(.md-avatar-icon),md-list-item ._md-list-item-inner>md-icon:first-child:not(.md-avatar-icon),md-list-item>div.md-primary>md-icon:not(.md-avatar-icon),md-list-item>div.md-secondary>md-icon:not(.md-avatar-icon),md-list-item>md-icon.md-secondary:not(.md-avatar-icon),md-list-item>md-icon:first-child:not(.md-avatar-icon){width:24px;margin-top:16px;margin-bottom:12px;box-sizing:content-box}md-list-item ._md-list-item-inner md-checkbox.md-secondary,md-list-item ._md-list-item-inner>div.md-primary>md-checkbox,md-list-item ._md-list-item-inner>div.md-secondary>md-checkbox,md-list-item ._md-list-item-inner>md-checkbox,md-list-item md-checkbox.md-secondary,md-list-item>div.md-primary>md-checkbox,md-list-item>div.md-secondary>md-checkbox,md-list-item>md-checkbox{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}md-list-item ._md-list-item-inner md-checkbox.md-secondary .md-label,md-list-item ._md-list-item-inner>div.md-primary>md-checkbox .md-label,md-list-item ._md-list-item-inner>div.md-secondary>md-checkbox .md-label,md-list-item ._md-list-item-inner>md-checkbox .md-label,md-list-item md-checkbox.md-secondary .md-label,md-list-item>div.md-primary>md-checkbox .md-label,md-list-item>div.md-secondary>md-checkbox .md-label,md-list-item>md-checkbox .md-label{display:none}md-list-item ._md-list-item-inner>md-icon:first-child:not(.md-avatar-icon),md-list-item>md-icon:first-child:not(.md-avatar-icon){margin-right:32px}[dir=rtl] md-list-item ._md-list-item-inner>md-icon:first-child:not(.md-avatar-icon),[dir=rtl] md-list-item>md-icon:first-child:not(.md-avatar-icon){margin-right:auto;margin-right:initial;margin-left:32px}md-list-item ._md-list-item-inner .md-avatar,md-list-item ._md-list-item-inner .md-avatar-icon,md-list-item .md-avatar,md-list-item .md-avatar-icon{margin-top:8px;margin-bottom:8px;margin-right:16px;border-radius:50%;box-sizing:content-box}[dir=rtl] md-list-item ._md-list-item-inner .md-avatar,[dir=rtl] md-list-item ._md-list-item-inner .md-avatar-icon,[dir=rtl] md-list-item .md-avatar,[dir=rtl] md-list-item .md-avatar-icon{margin-right:auto;margin-right:initial;margin-left:16px}md-list-item ._md-list-item-inner .md-avatar,md-list-item .md-avatar{-webkit-flex:none;-ms-flex:none;flex:none;width:40px;height:40px}md-list-item ._md-list-item-inner .md-avatar-icon,md-list-item .md-avatar-icon{padding:8px}md-list-item ._md-list-item-inner .md-avatar-icon svg,md-list-item .md-avatar-icon svg{width:24px;height:24px}md-list-item ._md-list-item-inner>md-checkbox,md-list-item>md-checkbox{width:24px;margin-left:3px;margin-right:29px;margin-top:16px}[dir=rtl] md-list-item ._md-list-item-inner>md-checkbox,[dir=rtl] md-list-item>md-checkbox{margin-left:29px;margin-right:3px}md-list-item ._md-list-item-inner ._md-secondary-container,md-list-item ._md-secondary-container{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;margin:auto 0 auto auto}[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container,[dir=rtl] md-list-item ._md-secondary-container{margin-right:auto;margin-left:0}md-list-item ._md-list-item-inner ._md-secondary-container .md-button:last-of-type,md-list-item ._md-list-item-inner ._md-secondary-container .md-icon-button:last-of-type,md-list-item ._md-secondary-container .md-button:last-of-type,md-list-item ._md-secondary-container .md-icon-button:last-of-type{margin-right:0}[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container .md-button:last-of-type,[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container .md-icon-button:last-of-type,[dir=rtl] md-list-item ._md-secondary-container .md-button:last-of-type,[dir=rtl] md-list-item ._md-secondary-container .md-icon-button:last-of-type{margin-right:auto;margin-right:initial;margin-left:0}md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox,md-list-item ._md-secondary-container md-checkbox{margin-top:0;margin-bottom:0}md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox:last-child,md-list-item ._md-secondary-container md-checkbox:last-child{width:24px;margin-right:0}[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container md-checkbox:last-child,[dir=rtl] md-list-item ._md-secondary-container md-checkbox:last-child{margin-right:auto;margin-right:initial;margin-left:0}md-list-item ._md-list-item-inner ._md-secondary-container md-switch,md-list-item ._md-secondary-container md-switch{margin-top:0;margin-bottom:0;margin-right:-6px}[dir=rtl] md-list-item ._md-list-item-inner ._md-secondary-container md-switch,[dir=rtl] md-list-item ._md-secondary-container md-switch{margin-right:auto;margin-right:initial;margin-left:-6px}md-list-item ._md-list-item-inner>._md-list-item-inner>p,md-list-item ._md-list-item-inner>p,md-list-item>._md-list-item-inner>p,md-list-item>p{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;margin:0}md-list-item.md-2-line,md-list-item.md-2-line>._md-no-style,md-list-item.md-3-line,md-list-item.md-3-line>._md-no-style{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}md-list-item.md-2-line.md-long-text,md-list-item.md-2-line>._md-no-style.md-long-text,md-list-item.md-3-line.md-long-text,md-list-item.md-3-line>._md-no-style.md-long-text{margin-top:8px;margin-bottom:8px}md-list-item.md-2-line .md-list-item-text,md-list-item.md-2-line>._md-no-style .md-list-item-text,md-list-item.md-3-line .md-list-item-text,md-list-item.md-3-line>._md-no-style .md-list-item-text{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;margin:auto;text-overflow:ellipsis;overflow:hidden}md-list-item.md-2-line .md-list-item-text.md-offset,md-list-item.md-2-line>._md-no-style .md-list-item-text.md-offset,md-list-item.md-3-line .md-list-item-text.md-offset,md-list-item.md-3-line>._md-no-style .md-list-item-text.md-offset{margin-left:56px}[dir=rtl] md-list-item.md-2-line .md-list-item-text.md-offset,[dir=rtl] md-list-item.md-2-line>._md-no-style .md-list-item-text.md-offset,[dir=rtl] md-list-item.md-3-line .md-list-item-text.md-offset,[dir=rtl] md-list-item.md-3-line>._md-no-style .md-list-item-text.md-offset{margin-left:auto;margin-left:initial;margin-right:56px}md-list-item.md-2-line .md-list-item-text h3,md-list-item.md-2-line>._md-no-style .md-list-item-text h3,md-list-item.md-3-line .md-list-item-text h3,md-list-item.md-3-line>._md-no-style .md-list-item-text h3{font-size:16px;font-weight:400;letter-spacing:.010em;margin:0;line-height:1.2em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}md-list-item.md-2-line .md-list-item-text h4,md-list-item.md-2-line>._md-no-style .md-list-item-text h4,md-list-item.md-3-line .md-list-item-text h4,md-list-item.md-3-line>._md-no-style .md-list-item-text h4{font-size:14px;letter-spacing:.010em;margin:3px 0 1px;font-weight:400;line-height:1.2em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}md-list-item.md-2-line .md-list-item-text p,md-list-item.md-2-line>._md-no-style .md-list-item-text p,md-list-item.md-3-line .md-list-item-text p,md-list-item.md-3-line>._md-no-style .md-list-item-text p{font-size:14px;font-weight:500;letter-spacing:.010em;margin:0;line-height:1.6em}md-list-item.md-2-line,md-list-item.md-2-line>._md-no-style{height:auto;min-height:72px}md-list-item.md-2-line div.md-button:first-child::before,md-list-item.md-2-line>._md-no-style div.md-button:first-child::before{content:'';min-height:72px;visibility:hidden;display:inline-block}md-list-item.md-2-line .md-avatar-icon,md-list-item.md-2-line>._md-no-style .md-avatar-icon,md-list-item.md-2-line>._md-no-style>.md-avatar,md-list-item.md-2-line>.md-avatar{margin-top:12px}md-list-item.md-2-line>._md-no-style>md-icon:first-child,md-list-item.md-2-line>md-icon:first-child{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}md-list-item.md-2-line .md-list-item-text,md-list-item.md-2-line>._md-no-style .md-list-item-text{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}md-list-item.md-3-line,md-list-item.md-3-line>._md-no-style{height:auto;min-height:88px}md-list-item.md-3-line div.md-button:first-child::before,md-list-item.md-3-line>._md-no-style div.md-button:first-child::before{content:'';min-height:88px;visibility:hidden;display:inline-block}md-list-item.md-3-line>._md-no-style>.md-avatar,md-list-item.md-3-line>._md-no-style>md-icon:first-child,md-list-item.md-3-line>.md-avatar,md-list-item.md-3-line>md-icon:first-child{margin-top:16px}._md-open-menu-container{position:fixed;left:0;top:0;z-index:100;opacity:0;border-radius:2px}._md-open-menu-container md-menu-divider{margin-top:4px;margin-bottom:4px;height:1px;min-height:1px;max-height:1px;width:100%}._md-open-menu-container md-menu-content>*{opacity:0}._md-open-menu-container:not(._md-clickable){pointer-events:none}._md-open-menu-container._md-active{opacity:1;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:200ms}._md-open-menu-container._md-active>md-menu-content>*{opacity:1;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:200ms;transition-delay:100ms}._md-open-menu-container._md-leave{opacity:0;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:250ms}md-menu-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;padding:8px 0;max-height:304px;overflow-y:auto}md-menu-content.md-dense{max-height:208px}md-menu-content.md-dense md-menu-item{height:32px;min-height:0}md-menu-item{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;min-height:48px;height:48px;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}md-menu-item>*{width:100%;margin:auto 0;padding-left:16px;padding-right:16px}md-menu-item>.md-button{border-radius:0;margin:auto 0;font-size:15px;text-transform:none;font-weight:400;height:100%;padding-left:16px;padding-right:16px;text-align:left;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:baseline;-ms-flex-align:baseline;align-items:baseline;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start;width:100%}[dir=rtl] md-menu-item>.md-button{text-align:right}md-menu-item>.md-button md-icon{margin:auto 16px auto 0}[dir=rtl] md-menu-item>.md-button md-icon{margin:auto 0 auto 16px}md-menu-item>.md-button p{display:inline-block;margin:auto}md-menu-item>.md-button span{margin-top:auto;margin-bottom:auto}md-menu-item>.md-button .md-ripple-container{border-radius:inherit}.md-menu{padding:8px 0}md-toolbar .md-menu{height:auto;margin:auto;padding:0}@media (max-width:959px){md-menu-content{min-width:112px}md-menu-content[width="3"]{min-width:168px}md-menu-content[width="4"]{min-width:224px}md-menu-content[width="5"]{min-width:280px}md-menu-content[width="6"]{min-width:336px}md-menu-content[width="7"]{min-width:392px}}@media (min-width:960px){md-menu-content{min-width:96px}md-menu-content[width="3"]{min-width:192px}md-menu-content[width="4"]{min-width:256px}md-menu-content[width="5"]{min-width:320px}md-menu-content[width="6"]{min-width:384px}md-menu-content[width="7"]{min-width:448px}}md-toolbar.md-menu-toolbar h2.md-toolbar-tools{line-height:1rem;height:auto;padding:28px 28px 12px}md-menu-bar{padding:0 20px;display:block;position:relative;z-index:2}md-menu-bar .md-menu{display:inline-block;padding:0;position:relative}md-menu-bar button{font-size:14px;padding:0 10px;margin:0;border:0;background-color:transparent;height:40px}md-menu-bar md-backdrop._md-menu-backdrop{z-index:-2}md-menu-content._md-menu-bar-menu.md-dense{max-height:none;padding:16px 0}md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent{position:relative}md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent>md-icon{position:absolute;padding:0;width:24px;top:6px;left:24px}[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent>md-icon{left:0;left:auto;left:initial;right:24px}md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent .md-menu>.md-button,md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent>.md-button{padding:0 32px 0 64px}[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent .md-menu>.md-button,[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item.md-indent>.md-button{padding:0 64px 0 32px}md-menu-content._md-menu-bar-menu.md-dense .md-button{min-height:0;height:32px;display:-webkit-flex;display:-ms-flexbox;display:flex}md-menu-content._md-menu-bar-menu.md-dense .md-button span{-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}md-menu-content._md-menu-bar-menu.md-dense .md-button span.md-alt-text{-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;margin:0 8px}md-menu-content._md-menu-bar-menu.md-dense md-menu-divider{margin:8px 0}md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button,md-menu-content._md-menu-bar-menu.md-dense md-menu-item>.md-button{text-align:left;text-align:start}[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button,[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense md-menu-item>.md-button{text-align:right}md-menu-content._md-menu-bar-menu.md-dense .md-menu{padding:0}md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button{position:relative;margin:0;width:100%;text-transform:none;font-weight:400;border-radius:0;padding-left:16px}[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button{padding-left:auto;padding-left:initial;padding-right:16px}md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button:after{display:block;content:'\25BC';position:absolute;top:0;speak:none;-webkit-transform:rotate(270deg) scaleY(.45) scaleX(.9);transform:rotate(270deg) scaleY(.45) scaleX(.9);right:28px}[dir=rtl] md-menu-content._md-menu-bar-menu.md-dense .md-menu>.md-button:after{-webkit-transform:rotate(90deg) scaleY(.45) scaleX(.9);transform:rotate(90deg) scaleY(.45) scaleX(.9);right:0;right:auto;right:initial;left:28px}.md-nav-bar{border-style:solid;border-width:0 0 1px;height:48px;position:relative}._md-nav-bar-list{outline:0;list-style:none;margin:0;padding:0}.md-nav-item:first-of-type{margin-left:8px}.md-button._md-nav-button{line-height:24px;margin:0 4px;padding:12px 16px;transition:background-color .35s cubic-bezier(.35,0,.25,1)}.md-button._md-nav-button:focus{outline:0}.md-button._md-nav-button:hover{background-color:inherit}md-nav-ink-bar{bottom:0;height:2px;left:auto;position:absolute;right:auto;background-color:#000}md-nav-ink-bar._md-left{transition:left .125s cubic-bezier(.35,0,.25,1),right .25s cubic-bezier(.35,0,.25,1)}md-nav-ink-bar._md-right{transition:left .25s cubic-bezier(.35,0,.25,1),right .125s cubic-bezier(.35,0,.25,1)}md-nav-extra-content{min-height:48px;padding-right:12px}.md-panel-outer-wrapper{height:100%;left:0;position:absolute;top:0;width:100%}._md-panel-hidden{display:none}._md-panel-fullscreen{border-radius:0;left:0;min-height:100%;min-width:100%;position:fixed;top:0}._md-panel-shown .md-panel{opacity:1;transition:none}.md-panel{opacity:0;position:fixed}.md-panel._md-panel-shown{opacity:1;transition:none}.md-panel._md-panel-animate-enter{opacity:1;transition:all .3s cubic-bezier(0,0,.2,1)}.md-panel._md-panel-animate-leave{opacity:1;transition:all .3s cubic-bezier(.4,0,1,1)}.md-panel._md-panel-animate-fade-out,.md-panel._md-panel-animate-scale-out{opacity:0}.md-panel._md-panel-backdrop{height:100%;position:absolute;width:100%}.md-panel._md-opaque-enter{opacity:.48;transition:opacity .3s cubic-bezier(0,0,.2,1)}.md-panel._md-opaque-leave{transition:opacity .3s cubic-bezier(.4,0,1,1)}md-progress-linear{display:block;position:relative;width:100%;height:5px;padding-top:0!important;margin-bottom:0!important}md-progress-linear._md-progress-linear-disabled{visibility:hidden}md-progress-linear ._md-container{display:block;position:relative;overflow:hidden;width:100%;height:5px;-webkit-transform:translate(0,0) scale(1,1);transform:translate(0,0) scale(1,1)}md-progress-linear ._md-container ._md-bar{position:absolute;left:0;top:0;bottom:0;width:100%;height:5px}md-progress-linear ._md-container ._md-dashed:before{content:"";display:none;position:absolute;margin-top:0;height:5px;width:100%;background-color:transparent;background-size:10px 10px!important;background-position:0 -23px}md-progress-linear ._md-container ._md-bar1,md-progress-linear ._md-container ._md-bar2{transition:-webkit-transform .2s linear;transition:transform .2s linear}md-progress-linear ._md-container._md-mode-query ._md-bar1{display:none}md-progress-linear ._md-container._md-mode-query ._md-bar2{transition:all .2s linear;-webkit-animation:query .8s infinite cubic-bezier(.39,.575,.565,1);animation:query .8s infinite cubic-bezier(.39,.575,.565,1)}md-progress-linear ._md-container._md-mode-determinate ._md-bar1{display:none}md-progress-linear ._md-container._md-mode-indeterminate ._md-bar1{-webkit-animation:md-progress-linear-indeterminate-scale-1 4s infinite,md-progress-linear-indeterminate-1 4s infinite;animation:md-progress-linear-indeterminate-scale-1 4s infinite,md-progress-linear-indeterminate-1 4s infinite}md-progress-linear ._md-container._md-mode-indeterminate ._md-bar2{-webkit-animation:md-progress-linear-indeterminate-scale-2 4s infinite,md-progress-linear-indeterminate-2 4s infinite;animation:md-progress-linear-indeterminate-scale-2 4s infinite,md-progress-linear-indeterminate-2 4s infinite}md-progress-linear ._md-container.ng-hide ._md-progress-linear-disabled md-progress-linear ._md-container{-webkit-animation:none;animation:none}md-progress-linear ._md-container.ng-hide ._md-progress-linear-disabled md-progress-linear ._md-container ._md-bar1,md-progress-linear ._md-container.ng-hide ._md-progress-linear-disabled md-progress-linear ._md-container ._md-bar2{-webkit-animation-name:none;animation-name:none}md-progress-linear ._md-container._md-mode-buffer{background-color:transparent!important;transition:all .2s linear}md-progress-linear ._md-container._md-mode-buffer ._md-dashed:before{display:block;-webkit-animation:buffer 3s infinite linear;animation:buffer 3s infinite linear}@-webkit-keyframes query{0%{opacity:1;-webkit-transform:translateX(35%) scale(.3,1);transform:translateX(35%) scale(.3,1)}100%{opacity:0;-webkit-transform:translateX(-50%) scale(0,1);transform:translateX(-50%) scale(0,1)}}@keyframes query{0%{opacity:1;-webkit-transform:translateX(35%) scale(.3,1);transform:translateX(35%) scale(.3,1)}100%{opacity:0;-webkit-transform:translateX(-50%) scale(0,1);transform:translateX(-50%) scale(0,1)}}@-webkit-keyframes buffer{0%{opacity:1;background-position:0 -23px}50%{opacity:0}100%{opacity:1;background-position:-200px -23px}}@keyframes buffer{0%{opacity:1;background-position:0 -23px}50%{opacity:0}100%{opacity:1;background-position:-200px -23px}}@-webkit-keyframes md-progress-linear-indeterminate-scale-1{0%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:linear;animation-timing-function:linear}36.6%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:cubic-bezier(.33473,.12482,.78584,1);animation-timing-function:cubic-bezier(.33473,.12482,.78584,1)}69.15%{-webkit-transform:scaleX(.83);transform:scaleX(.83);-webkit-animation-timing-function:cubic-bezier(.22573,0,.23365,1.37098);animation-timing-function:cubic-bezier(.22573,0,.23365,1.37098)}100%{-webkit-transform:scaleX(.1);transform:scaleX(.1)}}@keyframes md-progress-linear-indeterminate-scale-1{0%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:linear;animation-timing-function:linear}36.6%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:cubic-bezier(.33473,.12482,.78584,1);animation-timing-function:cubic-bezier(.33473,.12482,.78584,1)}69.15%{-webkit-transform:scaleX(.83);transform:scaleX(.83);-webkit-animation-timing-function:cubic-bezier(.22573,0,.23365,1.37098);animation-timing-function:cubic-bezier(.22573,0,.23365,1.37098)}100%{-webkit-transform:scaleX(.1);transform:scaleX(.1)}}@-webkit-keyframes md-progress-linear-indeterminate-1{0%{left:-105.16667%;-webkit-animation-timing-function:linear;animation-timing-function:linear}20%{left:-105.16667%;-webkit-animation-timing-function:cubic-bezier(.5,0,.70173,.49582);animation-timing-function:cubic-bezier(.5,0,.70173,.49582)}69.15%{left:21.5%;-webkit-animation-timing-function:cubic-bezier(.30244,.38135,.55,.95635);animation-timing-function:cubic-bezier(.30244,.38135,.55,.95635)}100%{left:95.44444%}}@keyframes md-progress-linear-indeterminate-1{0%{left:-105.16667%;-webkit-animation-timing-function:linear;animation-timing-function:linear}20%{left:-105.16667%;-webkit-animation-timing-function:cubic-bezier(.5,0,.70173,.49582);animation-timing-function:cubic-bezier(.5,0,.70173,.49582)}69.15%{left:21.5%;-webkit-animation-timing-function:cubic-bezier(.30244,.38135,.55,.95635);animation-timing-function:cubic-bezier(.30244,.38135,.55,.95635)}100%{left:95.44444%}}@-webkit-keyframes md-progress-linear-indeterminate-scale-2{0%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:cubic-bezier(.20503,.05705,.57661,.45397);animation-timing-function:cubic-bezier(.20503,.05705,.57661,.45397)}19.15%{-webkit-transform:scaleX(.57);transform:scaleX(.57);-webkit-animation-timing-function:cubic-bezier(.15231,.19643,.64837,1.00432);animation-timing-function:cubic-bezier(.15231,.19643,.64837,1.00432)}44.15%{-webkit-transform:scaleX(.91);transform:scaleX(.91);-webkit-animation-timing-function:cubic-bezier(.25776,-.00316,.21176,1.38179);animation-timing-function:cubic-bezier(.25776,-.00316,.21176,1.38179)}100%{-webkit-transform:scaleX(.1);transform:scaleX(.1)}}@keyframes md-progress-linear-indeterminate-scale-2{0%{-webkit-transform:scaleX(.1);transform:scaleX(.1);-webkit-animation-timing-function:cubic-bezier(.20503,.05705,.57661,.45397);animation-timing-function:cubic-bezier(.20503,.05705,.57661,.45397)}19.15%{-webkit-transform:scaleX(.57);transform:scaleX(.57);-webkit-animation-timing-function:cubic-bezier(.15231,.19643,.64837,1.00432);animation-timing-function:cubic-bezier(.15231,.19643,.64837,1.00432)}44.15%{-webkit-transform:scaleX(.91);transform:scaleX(.91);-webkit-animation-timing-function:cubic-bezier(.25776,-.00316,.21176,1.38179);animation-timing-function:cubic-bezier(.25776,-.00316,.21176,1.38179)}100%{-webkit-transform:scaleX(.1);transform:scaleX(.1)}}@-webkit-keyframes md-progress-linear-indeterminate-2{0%{left:-54.88889%;-webkit-animation-timing-function:cubic-bezier(.15,0,.51506,.40968);animation-timing-function:cubic-bezier(.15,0,.51506,.40968)}25%{left:-17.25%;-webkit-animation-timing-function:cubic-bezier(.31033,.28406,.8,.73372);animation-timing-function:cubic-bezier(.31033,.28406,.8,.73372)}48.35%{left:29.5%;-webkit-animation-timing-function:cubic-bezier(.4,.62703,.6,.90203);animation-timing-function:cubic-bezier(.4,.62703,.6,.90203)}100%{left:117.38889%}}@keyframes md-progress-linear-indeterminate-2{0%{left:-54.88889%;-webkit-animation-timing-function:cubic-bezier(.15,0,.51506,.40968);animation-timing-function:cubic-bezier(.15,0,.51506,.40968)}25%{left:-17.25%;-webkit-animation-timing-function:cubic-bezier(.31033,.28406,.8,.73372);animation-timing-function:cubic-bezier(.31033,.28406,.8,.73372)}48.35%{left:29.5%;-webkit-animation-timing-function:cubic-bezier(.4,.62703,.6,.90203);animation-timing-function:cubic-bezier(.4,.62703,.6,.90203)}100%{left:117.38889%}}@-webkit-keyframes indeterminate-rotate{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes indeterminate-rotate{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}md-progress-circular{position:relative}md-progress-circular._md-progress-circular-disabled{visibility:hidden}md-progress-circular._md-mode-indeterminate svg{-webkit-animation:indeterminate-rotate 2.9s linear infinite;animation:indeterminate-rotate 2.9s linear infinite}md-progress-circular svg{position:absolute;overflow:visible;top:0;left:0}md-radio-button{box-sizing:border-box;display:block;margin-bottom:16px;white-space:nowrap;cursor:pointer;position:relative}md-radio-button[disabled],md-radio-button[disabled] ._md-container{cursor:default}md-radio-button ._md-container{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);box-sizing:border-box;display:inline-block;width:20px;height:20px;cursor:pointer;left:0;right:auto}[dir=rtl] md-radio-button ._md-container{left:auto;right:0}md-radio-button ._md-container .md-ripple-container{position:absolute;display:block;width:auto;height:auto;left:-15px;top:-15px;right:-15px;bottom:-15px}md-radio-button ._md-container:before{box-sizing:border-box;background-color:transparent;border-radius:50%;content:'';position:absolute;display:block;height:auto;left:0;top:0;right:0;bottom:0;transition:all .5s;width:auto}md-radio-button.md-align-top-left>div._md-container{top:12px}md-radio-button ._md-off{box-sizing:border-box;position:absolute;top:0;left:0;width:20px;height:20px;border-style:solid;border-width:2px;border-radius:50%;transition:border-color ease .28s}md-radio-button ._md-on{box-sizing:border-box;position:absolute;top:0;left:0;width:20px;height:20px;border-radius:50%;transition:-webkit-transform ease .28s;transition:transform ease .28s;-webkit-transform:scale(0);transform:scale(0)}md-radio-button.md-checked ._md-on{-webkit-transform:scale(.5);transform:scale(.5)}md-radio-button ._md-label{box-sizing:border-box;position:relative;display:inline-block;margin-left:30px;margin-right:0;vertical-align:middle;white-space:normal;pointer-events:none;width:auto}[dir=rtl] md-radio-button ._md-label{margin-left:0;margin-right:30px}md-radio-group.layout-column md-radio-button,md-radio-group.layout-gt-lg-column md-radio-button,md-radio-group.layout-gt-md-column md-radio-button,md-radio-group.layout-gt-sm-column md-radio-button,md-radio-group.layout-gt-xs-column md-radio-button,md-radio-group.layout-lg-column md-radio-button,md-radio-group.layout-md-column md-radio-button,md-radio-group.layout-sm-column md-radio-button,md-radio-group.layout-xl-column md-radio-button,md-radio-group.layout-xs-column md-radio-button{margin-bottom:16px}md-radio-group.layout-gt-lg-row md-radio-button,md-radio-group.layout-gt-md-row md-radio-button,md-radio-group.layout-gt-sm-row md-radio-button,md-radio-group.layout-gt-xs-row md-radio-button,md-radio-group.layout-lg-row md-radio-button,md-radio-group.layout-md-row md-radio-button,md-radio-group.layout-row md-radio-button,md-radio-group.layout-sm-row md-radio-button,md-radio-group.layout-xl-row md-radio-button,md-radio-group.layout-xs-row md-radio-button{margin:0 16px 0 0}[dir=rtl] md-radio-group.layout-gt-lg-row md-radio-button,[dir=rtl] md-radio-group.layout-gt-md-row md-radio-button,[dir=rtl] md-radio-group.layout-gt-sm-row md-radio-button,[dir=rtl] md-radio-group.layout-gt-xs-row md-radio-button,[dir=rtl] md-radio-group.layout-lg-row md-radio-button,[dir=rtl] md-radio-group.layout-md-row md-radio-button,[dir=rtl] md-radio-group.layout-row md-radio-button,[dir=rtl] md-radio-group.layout-sm-row md-radio-button,[dir=rtl] md-radio-group.layout-xl-row md-radio-button,[dir=rtl] md-radio-group.layout-xs-row md-radio-button{margin-left:16px;margin-right:0}md-radio-group.layout-gt-lg-row md-radio-button:last-of-type,md-radio-group.layout-gt-md-row md-radio-button:last-of-type,md-radio-group.layout-gt-sm-row md-radio-button:last-of-type,md-radio-group.layout-gt-xs-row md-radio-button:last-of-type,md-radio-group.layout-lg-row md-radio-button:last-of-type,md-radio-group.layout-md-row md-radio-button:last-of-type,md-radio-group.layout-row md-radio-button:last-of-type,md-radio-group.layout-sm-row md-radio-button:last-of-type,md-radio-group.layout-xl-row md-radio-button:last-of-type,md-radio-group.layout-xs-row md-radio-button:last-of-type{margin-left:0;margin-right:0}md-radio-group:focus{outline:0}md-radio-group.md-focused .md-checked ._md-container:before{left:-8px;top:-8px;right:-8px;bottom:-8px}.md-inline-form md-radio-group{margin:18px 0 19px}.md-inline-form md-radio-group md-radio-button{display:inline-block;height:30px;padding:2px;box-sizing:border-box;margin-top:0;margin-bottom:0}@media screen and (-ms-high-contrast:active){md-radio-button.md-default-theme ._md-on{background-color:#fff}}._md-select-menu-container{position:fixed;left:0;top:0;z-index:90;opacity:0;display:none}._md-select-menu-container:not(._md-clickable){pointer-events:none}._md-select-menu-container md-progress-circular{display:table;margin:24px auto!important}._md-select-menu-container._md-active{display:block;opacity:1}._md-select-menu-container._md-active md-select-menu{transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:150ms}._md-select-menu-container._md-active md-select-menu>*{opacity:1;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:150ms;transition-delay:100ms}._md-select-menu-container._md-leave{opacity:0;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:250ms}md-input-container>md-select{margin:0;-webkit-order:2;-ms-flex-order:2;order:2}md-select{display:-webkit-flex;display:-ms-flexbox;display:flex;margin:20px 0 26px}md-select[disabled] ._md-select-value{background-position:0 bottom;background-size:4px 1px;background-repeat:repeat-x;margin-bottom:-1px}md-select:focus{outline:0}md-select[disabled]:hover{cursor:default}md-select:not([disabled]):hover{cursor:pointer}md-select:not([disabled]).ng-invalid.ng-dirty ._md-select-value{border-bottom:2px solid;padding-bottom:0}md-select:not([disabled]):focus ._md-select-value{border-bottom-width:2px;border-bottom-style:solid;padding-bottom:0}._md-select-value{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;padding:2px 2px 1px;border-bottom-width:1px;border-bottom-style:solid;background-color:transparent;position:relative;box-sizing:content-box;min-width:64px;min-height:26px;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}._md-select-value>span:not(._md-select-icon){max-width:100%;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;-webkit-transform:translate3d(0,2px,0);transform:translate3d(0,2px,0);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._md-select-value>span:not(._md-select-icon) ._md-text{display:inline}._md-select-value ._md-select-icon{display:block;-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;text-align:end;width:24px;margin:0 4px;-webkit-transform:translate3d(0,1px,0);transform:translate3d(0,1px,0)}._md-select-value ._md-select-icon:after{display:block;content:'\25BC';position:relative;top:2px;speak:none;-webkit-transform:scaleY(.6) scaleX(1);transform:scaleY(.6) scaleX(1)}._md-select-value._md-select-placeholder{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-order:1;-ms-flex-order:1;order:1;pointer-events:none;-webkit-font-smoothing:antialiased;padding-left:2px;z-index:1}md-select-menu{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;box-shadow:0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12);max-height:256px;min-height:48px;overflow-y:hidden;-webkit-transform-origin:left top;transform-origin:left top;-webkit-transform:scale(1);transform:scale(1)}md-select-menu.md-reverse{-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}md-select-menu:not(._md-overflow) md-content{padding-top:8px;padding-bottom:8px}[dir=rtl] md-select-menu{-webkit-transform-origin:right top;transform-origin:right top}md-select-menu md-content{min-width:136px;min-height:48px;max-height:256px;overflow-y:auto}md-select-menu>*{opacity:0}md-option{cursor:pointer;position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;width:auto;transition:background .15s linear;padding:0 16px;height:48px}md-option[disabled]{cursor:default}md-option:focus{outline:0}md-option ._md-text{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16px}md-optgroup{display:block}md-optgroup label{display:block;font-size:14px;text-transform:uppercase;padding:16px;font-weight:500}md-optgroup md-option{padding-left:32px;padding-right:32px}@media screen and (-ms-high-contrast:active){._md-select-backdrop{background-color:transparent}md-select-menu{border:1px solid #fff}}md-select-menu[multiple] md-option._md-checkbox-enabled{padding-left:40px;padding-right:16px}[dir=rtl] md-select-menu[multiple] md-option._md-checkbox-enabled{padding-left:16px;padding-right:40px}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);box-sizing:border-box;display:inline-block;width:20px;height:20px;left:0;right:auto}[dir=rtl] md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container{left:auto;right:0}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container:before{box-sizing:border-box;background-color:transparent;border-radius:50%;content:'';position:absolute;display:block;height:auto;left:0;top:0;right:0;bottom:0;transition:all .5s;width:auto}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container:after{box-sizing:border-box;content:'';position:absolute;top:-10px;right:-10px;bottom:-10px;left:-10px}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container .md-ripple-container{position:absolute;display:block;width:auto;height:auto;left:-15px;top:-15px;right:-15px;bottom:-15px}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-icon{box-sizing:border-box;transition:240ms;position:absolute;top:0;left:0;width:20px;height:20px;border-width:2px;border-style:solid;border-radius:2px}md-select-menu[multiple] md-option._md-checkbox-enabled[selected] ._md-icon{border:none}md-select-menu[multiple] md-option._md-checkbox-enabled[selected] ._md-icon:after{box-sizing:border-box;-webkit-transform:rotate(45deg);transform:rotate(45deg);position:absolute;left:6.67px;top:2.22px;display:table;width:6.67px;height:13.33px;border-width:2px;border-style:solid;border-top:0;border-left:0;content:''}md-select-menu[multiple] md-option._md-checkbox-enabled[disabled]{cursor:default}md-select-menu[multiple] md-option._md-checkbox-enabled.md-indeterminate ._md-icon:after{box-sizing:border-box;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:table;width:12px;height:2px;border-width:2px;border-style:solid;border-top:0;border-left:0;content:''}md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container{margin-left:10.67px;margin-right:auto}[dir=rtl] md-select-menu[multiple] md-option._md-checkbox-enabled ._md-container{margin-left:auto;margin-right:10.67px}md-sidenav{box-sizing:border-box;position:absolute;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;z-index:60;width:320px;max-width:320px;bottom:0;overflow:auto;-webkit-overflow-scrolling:touch}md-sidenav ul{list-style:none}md-sidenav._md-closed{display:none}md-sidenav._md-closed-add,md-sidenav._md-closed-remove{display:-webkit-flex;display:-ms-flexbox;display:flex;transition:.2s ease-in all}md-sidenav._md-closed-add._md-closed-add-active,md-sidenav._md-closed-remove._md-closed-remove-active{transition:all .4s cubic-bezier(.25,.8,.25,1)}md-sidenav._md-locked-open,md-sidenav._md-locked-open-add,md-sidenav._md-locked-open-remove,md-sidenav._md-locked-open-remove._md-closed,md-sidenav._md-locked-open._md-closed,md-sidenav._md-locked-open._md-closed.md-sidenav-left,md-sidenav._md-locked-open._md-closed.md-sidenav-right{position:static;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-sidenav._md-locked-open-remove-active{transition:width .3s cubic-bezier(.55,0,.55,.2),min-width .3s cubic-bezier(.55,0,.55,.2);width:0!important;min-width:0!important}md-sidenav._md-closed._md-locked-open-add{width:0!important;min-width:0!important;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-sidenav._md-closed._md-locked-open-add-active{transition:width .3s cubic-bezier(.55,0,.55,.2),min-width .3s cubic-bezier(.55,0,.55,.2);width:320px;min-width:320px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}._md-sidenav-backdrop._md-locked-open{display:none}.md-sidenav-left,md-sidenav{left:0;top:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.md-sidenav-left._md-closed,md-sidenav._md-closed{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.md-sidenav-right{left:100%;top:0;-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}.md-sidenav-right._md-closed{-webkit-transform:translate(0,0);transform:translate(0,0)}@media (min-width:600px){md-sidenav{max-width:400px}}@media (max-width:456px){md-sidenav{width:calc(100% - 56px);min-width:calc(100% - 56px);max-width:calc(100% - 56px)}}@media screen and (-ms-high-contrast:active){.md-sidenav-left,md-sidenav{border-right:1px solid #fff}.md-sidenav-right{border-left:1px solid #fff}}@-webkit-keyframes sliderFocusThumb{0%{-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1);transform:scale(1)}100%{-webkit-transform:scale(.7);transform:scale(.7)}}@keyframes sliderFocusThumb{0%{-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1);transform:scale(1)}100%{-webkit-transform:scale(.7);transform:scale(.7)}}@-webkit-keyframes sliderDiscreteFocusThumb{0%{-webkit-transform:scale(.7);transform:scale(.7)}50%{-webkit-transform:scale(.8);transform:scale(.8)}100%{-webkit-transform:scale(0);transform:scale(0)}}@keyframes sliderDiscreteFocusThumb{0%{-webkit-transform:scale(.7);transform:scale(.7)}50%{-webkit-transform:scale(.8);transform:scale(.8)}100%{-webkit-transform:scale(0);transform:scale(0)}}@-webkit-keyframes sliderDiscreteFocusRing{0%{-webkit-transform:scale(.7);transform:scale(.7);opacity:0}50%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{-webkit-transform:scale(0);transform:scale(0)}}@keyframes sliderDiscreteFocusRing{0%{-webkit-transform:scale(.7);transform:scale(.7);opacity:0}50%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{-webkit-transform:scale(0);transform:scale(0)}}md-slider{height:48px;min-width:128px;position:relative;margin-left:4px;margin-right:4px;padding:0;display:block;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-slider *,md-slider :after{box-sizing:border-box}md-slider ._md-slider-wrapper{outline:0;width:100%;height:100%}md-slider ._md-slider-content{position:relative}md-slider ._md-track-container{width:100%;position:absolute;top:23px;height:2px}md-slider ._md-track{position:absolute;left:0;right:0;height:100%}md-slider ._md-track-fill{transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:width,height}md-slider ._md-track-ticks{position:absolute;left:0;right:0;height:100%}md-slider ._md-track-ticks canvas{width:100%;height:100%}md-slider ._md-thumb-container{position:absolute;left:0;top:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:left,bottom}md-slider ._md-thumb{z-index:1;position:absolute;left:-10px;top:14px;width:20px;height:20px;border-radius:20px;-webkit-transform:scale(.7);transform:scale(.7);transition:all .4s cubic-bezier(.25,.8,.25,1)}md-slider ._md-thumb:after{content:'';position:absolute;width:20px;height:20px;border-radius:20px;border-width:3px;border-style:solid;transition:inherit}md-slider ._md-sign{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;position:absolute;left:-14px;top:-17px;width:28px;height:28px;border-radius:28px;-webkit-transform:scale(.4) translate3d(0,67.5px,0);transform:scale(.4) translate3d(0,67.5px,0);transition:all .3s cubic-bezier(.35,0,.25,1)}md-slider ._md-sign:after{position:absolute;content:'';left:0;border-radius:16px;top:19px;border-left:14px solid transparent;border-right:14px solid transparent;border-top-width:16px;border-top-style:solid;opacity:0;-webkit-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0);transition:all .2s cubic-bezier(.35,0,.25,1)}md-slider ._md-sign ._md-thumb-text{z-index:1;font-size:12px;font-weight:700}md-slider ._md-focus-ring{position:absolute;left:-17px;top:7px;width:34px;height:34px;border-radius:34px;-webkit-transform:scale(.7);transform:scale(.7);opacity:0;transition:all .35s cubic-bezier(.35,0,.25,1)}md-slider ._md-disabled-thumb{position:absolute;left:-14px;top:10px;width:28px;height:28px;border-radius:28px;-webkit-transform:scale(.5);transform:scale(.5);border-width:4px;border-style:solid;display:none}md-slider._md-min ._md-sign{opacity:0}md-slider:focus{outline:0}md-slider._md-dragging ._md-thumb-container,md-slider._md-dragging ._md-track-fill{transition:none}md-slider:not([md-discrete]) ._md-sign,md-slider:not([md-discrete]) ._md-track-ticks{display:none}md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper ._md-thumb:hover{-webkit-transform:scale(.8);transform:scale(.8)}md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper.md-focused ._md-focus-ring{-webkit-transform:scale(1);transform:scale(1);opacity:1}md-slider:not([md-discrete]):not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb{-webkit-animation:sliderFocusThumb .7s cubic-bezier(.35,0,.25,1);animation:sliderFocusThumb .7s cubic-bezier(.35,0,.25,1)}md-slider:not([md-discrete]):not([disabled])._md-active ._md-slider-wrapper ._md-thumb{-webkit-transform:scale(1);transform:scale(1)}md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-focus-ring{-webkit-transform:scale(0);transform:scale(0);-webkit-animation:sliderDiscreteFocusRing .5s cubic-bezier(.35,0,.25,1);animation:sliderDiscreteFocusRing .5s cubic-bezier(.35,0,.25,1)}md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb{-webkit-animation:sliderDiscreteFocusThumb .5s cubic-bezier(.35,0,.25,1);animation:sliderDiscreteFocusThumb .5s cubic-bezier(.35,0,.25,1)}md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-thumb,md-slider[md-discrete]:not([disabled])._md-active ._md-thumb{-webkit-transform:scale(0);transform:scale(0)}md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-sign,md-slider[md-discrete]:not([disabled]) ._md-slider-wrapper.md-focused ._md-sign:after,md-slider[md-discrete]:not([disabled])._md-active ._md-sign,md-slider[md-discrete]:not([disabled])._md-active ._md-sign:after{opacity:1;-webkit-transform:translate3d(0,0,0) scale(1);transform:translate3d(0,0,0) scale(1)}md-slider[md-discrete][disabled][readonly] ._md-thumb{-webkit-transform:scale(0);transform:scale(0)}md-slider[md-discrete][disabled][readonly] ._md-sign,md-slider[md-discrete][disabled][readonly] ._md-sign:after{opacity:1;-webkit-transform:translate3d(0,0,0) scale(1);transform:translate3d(0,0,0) scale(1)}md-slider[disabled] ._md-track-fill{display:none}md-slider[disabled] ._md-track-ticks,md-slider[disabled]:not([readonly]) ._md-sign{opacity:0}md-slider[disabled] ._md-thumb{-webkit-transform:scale(.5);transform:scale(.5)}md-slider[disabled] ._md-disabled-thumb{display:block}md-slider[md-vertical]{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-height:128px;min-width:0}md-slider[md-vertical] ._md-slider-wrapper{-webkit-flex:1;-ms-flex:1;flex:1;padding-top:12px;padding-bottom:12px;width:48px;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}md-slider[md-vertical] ._md-track-container{height:100%;width:2px;top:0;left:calc(50% - (2px / 2))}md-slider[md-vertical] ._md-thumb-container{top:auto;margin-bottom:23px;left:calc(50% - 1px);bottom:0}md-slider[md-vertical] ._md-thumb-container ._md-thumb:after{left:1px}md-slider[md-vertical] ._md-thumb-container ._md-focus-ring{left:-16px}md-slider[md-vertical] ._md-track-fill{bottom:0}md-slider[md-vertical][md-discrete] ._md-sign{left:-40px;top:9.5px;-webkit-transform:scale(.4) translate3d(67.5px,0,0);transform:scale(.4) translate3d(67.5px,0,0)}md-slider[md-vertical][md-discrete] ._md-sign:after{top:9.5px;left:19px;border-top:14px solid transparent;border-right:0;border-bottom:14px solid transparent;border-left-width:16px;border-left-style:solid;opacity:0;-webkit-transform:translate3d(0,-8px,0);transform:translate3d(0,-8px,0);transition:all .2s ease-in-out}md-slider[md-vertical][md-discrete] ._md-sign ._md-thumb-text{z-index:1;font-size:12px;font-weight:700}md-slider[md-vertical][md-discrete] .md-focused ._md-sign:after,md-slider[md-vertical][md-discrete]._md-active ._md-sign:after,md-slider[md-vertical][md-discrete][disabled][readonly] ._md-sign:after{top:0}md-slider[md-vertical][disabled][readonly] ._md-thumb{-webkit-transform:scale(0);transform:scale(0)}md-slider[md-vertical][disabled][readonly] ._md-sign,md-slider[md-vertical][disabled][readonly] ._md-sign:after{opacity:1;-webkit-transform:translate3d(0,0,0) scale(1);transform:translate3d(0,0,0) scale(1)}md-slider[md-invert]:not([md-vertical]) ._md-track-fill{left:auto;right:0}md-slider[md-invert][md-vertical] ._md-track-fill{bottom:auto;top:0}md-slider-container{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-slider-container>:first-child:not(md-slider),md-slider-container>:last-child:not(md-slider){min-width:25px;max-width:42px;height:25px;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:color,max-width}md-slider-container>:first-child:not(md-slider){margin-right:16px}md-slider-container>:last-child:not(md-slider){margin-left:16px}md-slider-container[md-vertical]{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}md-slider-container[md-vertical]>:first-child:not(md-slider),md-slider-container[md-vertical]>:last-child:not(md-slider){margin-right:0;margin-left:0;text-align:center}md-slider-container md-input-container input[type=number]{text-align:center;padding-left:15px;height:50px;margin-top:-25px}@media screen and (-ms-high-contrast:active){md-slider.md-default-theme ._md-track{border-bottom:1px solid #fff}}._md-sticky-clone{z-index:2;top:0;left:0;right:0;position:absolute!important;-webkit-transform:translate3d(-9999px,-9999px,0);transform:translate3d(-9999px,-9999px,0)}._md-sticky-clone[sticky-state=active]{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}._md-sticky-clone[sticky-state=active]:not(.md-sticky-no-effect) ._md-subheader-inner{-webkit-animation:subheaderStickyHoverIn .3s ease-out both;animation:subheaderStickyHoverIn .3s ease-out both}@-webkit-keyframes subheaderStickyHoverIn{0%{box-shadow:0 0 0 0 transparent}100%{box-shadow:0 2px 4px 0 rgba(0,0,0,.16)}}@keyframes subheaderStickyHoverIn{0%{box-shadow:0 0 0 0 transparent}100%{box-shadow:0 2px 4px 0 rgba(0,0,0,.16)}}@-webkit-keyframes subheaderStickyHoverOut{0%{box-shadow:0 2px 4px 0 rgba(0,0,0,.16)}100%{box-shadow:0 0 0 0 transparent}}@keyframes subheaderStickyHoverOut{0%{box-shadow:0 2px 4px 0 rgba(0,0,0,.16)}100%{box-shadow:0 0 0 0 transparent}}._md-subheader-wrapper:not(.md-sticky-no-effect){transition:.2s ease-out margin}._md-subheader-wrapper:not(.md-sticky-no-effect) .md-subheader{margin:0}._md-subheader-wrapper:not(.md-sticky-no-effect).md-sticky-clone{z-index:2}._md-subheader-wrapper:not(.md-sticky-no-effect)[sticky-state=active]{margin-top:-2px}._md-subheader-wrapper:not(.md-sticky-no-effect):not(.md-sticky-clone)[sticky-prev-state=active] ._md-subheader-inner:after{-webkit-animation:subheaderStickyHoverOut .3s ease-out both;animation:subheaderStickyHoverOut .3s ease-out both}.md-subheader{display:block;font-size:14px;font-weight:500;line-height:1em;margin:0;position:relative}.md-subheader ._md-subheader-inner{display:block;padding:16px}.md-subheader ._md-subheader-content{display:block;z-index:1;position:relative}.md-inline-form md-switch{margin-top:18px;margin-bottom:19px}md-switch{margin:16px;margin-left:inherit;white-space:nowrap;cursor:pointer;outline:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;height:30px;line-height:28px;-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:-webkit-flex;display:-ms-flexbox;display:flex}[dir=rtl] md-switch{margin-left:16px;margin-right:inherit}md-switch:last-of-type{margin-left:inherit;margin-right:0}[dir=rtl] md-switch:last-of-type{margin-left:0;margin-right:inherit}md-switch[disabled],md-switch[disabled] ._md-container{cursor:default}md-switch ._md-container{cursor:-webkit-grab;cursor:grab;width:36px;height:24px;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-right:8px;float:left}[dir=rtl] md-switch ._md-container{margin-right:auto;margin-right:initial;margin-left:8px}md-switch:not([disabled]) ._md-dragging,md-switch:not([disabled])._md-dragging ._md-container{cursor:-webkit-grabbing;cursor:grabbing}md-switch.md-focused:not([disabled]) ._md-thumb:before{left:-8px;top:-8px;right:-8px;bottom:-8px}md-switch.md-focused:not([disabled]):not(.md-checked) ._md-thumb:before{background-color:rgba(0,0,0,.12)}md-switch ._md-label{border-color:transparent;border-width:0;float:left}md-switch ._md-bar{left:1px;width:34px;top:5px;height:14px;border-radius:8px;position:absolute}md-switch ._md-thumb-container{top:2px;left:0;width:16px;position:absolute;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);z-index:1}md-switch.md-checked ._md-thumb-container{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}md-switch ._md-thumb{position:absolute;margin:0;left:0;top:0;outline:0;height:20px;width:20px;border-radius:50%;box-shadow:0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)}md-switch ._md-thumb:before{background-color:transparent;border-radius:50%;content:'';position:absolute;display:block;height:auto;left:0;top:0;right:0;bottom:0;transition:all .5s;width:auto}md-switch ._md-thumb .md-ripple-container{position:absolute;display:block;width:auto;height:auto;left:-20px;top:-20px;right:-20px;bottom:-20px}md-switch:not(._md-dragging) ._md-bar,md-switch:not(._md-dragging) ._md-thumb,md-switch:not(._md-dragging) ._md-thumb-container{transition:all .08s linear;transition-property:-webkit-transform,background-color;transition-property:transform,background-color}md-switch:not(._md-dragging) ._md-bar,md-switch:not(._md-dragging) ._md-thumb{transition-delay:.05s}@media screen and (-ms-high-contrast:active){md-switch.md-default-theme ._md-bar{background-color:#666}md-switch.md-default-theme.md-checked ._md-bar{background-color:#9E9E9E}md-switch.md-default-theme ._md-thumb{background-color:#fff}}@-webkit-keyframes md-tab-content-hide{0%,50%{opacity:1}100%{opacity:0}}@keyframes md-tab-content-hide{0%,50%{opacity:1}100%{opacity:0}}md-tab-data{position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1;opacity:0}md-tabs{display:block;margin:0;border-radius:2px;overflow:hidden;position:relative;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}md-tabs:not(.md-no-tab-content):not(.md-dynamic-height){min-height:248px}md-tabs[md-align-tabs=bottom]{padding-bottom:48px}md-tabs[md-align-tabs=bottom] md-tabs-wrapper{position:absolute;bottom:0;left:0;right:0;height:48px;z-index:2}md-tabs[md-align-tabs=bottom] md-tabs-content-wrapper{top:0;bottom:48px}md-tabs.md-dynamic-height md-tabs-content-wrapper{min-height:0;position:relative;top:auto;left:auto;right:auto;bottom:auto;overflow:visible}md-tabs.md-dynamic-height md-tab-content.md-active{position:relative}md-tabs[md-border-bottom] md-tabs-wrapper{border-width:0 0 1px;border-style:solid}md-tabs[md-border-bottom]:not(.md-dynamic-height) md-tabs-content-wrapper{top:49px}md-tabs-wrapper{display:block;position:relative;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-tabs-wrapper md-next-button,md-tabs-wrapper md-prev-button{height:100%;width:32px;position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);line-height:1em;z-index:2;cursor:pointer;font-size:16px;background:center center no-repeat;transition:all .5s cubic-bezier(.35,0,.25,1)}md-tabs-wrapper md-next-button:focus,md-tabs-wrapper md-prev-button:focus{outline:0}md-tabs-wrapper md-next-button.md-disabled,md-tabs-wrapper md-prev-button.md-disabled{opacity:.25;cursor:default}md-tabs-wrapper md-next-button.ng-leave,md-tabs-wrapper md-prev-button.ng-leave{transition:none}md-tabs-wrapper md-next-button md-icon,md-tabs-wrapper md-prev-button md-icon{position:absolute;top:50%;left:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}md-tabs-wrapper md-prev-button{left:0;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMjA4IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyIAkJIiBzdHlsZT0iZmlsbDp3aGl0ZTsiLz4gPHJlY3QgZmlsbD0ibm9uZSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ii8+IDwvZz4gPC9nPiA8ZyBpZD0iR3JpZCIgZGlzcGxheT0ibm9uZSI+IDxnIGRpc3BsYXk9ImlubGluZSI+IDwvZz4gPC9nPiA8L3N2Zz4NCg==)}[dir=rtl] md-tabs-wrapper md-prev-button{left:0;left:auto;left:initial;right:0}md-tabs-wrapper md-next-button{right:0;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMzM2IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTAsNiA4LjYsNy40IDEzLjIsMTIgOC42LDE2LjYgMTAsMTggMTYsMTIgCQkiIHN0eWxlPSJmaWxsOndoaXRlOyIvPiA8cmVjdCBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz4gPC9nPiA8L2c+IDxnIGlkPSJHcmlkIiBkaXNwbGF5PSJub25lIj4gPGcgZGlzcGxheT0iaW5saW5lIj4gPC9nPiA8L2c+IDwvc3ZnPg0K)}[dir=rtl] md-tabs-wrapper md-next-button{right:0;right:auto;right:initial;left:0}md-tabs-wrapper md-next-button md-icon{-webkit-transform:translate3d(-50%,-50%,0) rotate(180deg);transform:translate3d(-50%,-50%,0) rotate(180deg)}md-tabs-wrapper.md-stretch-tabs md-pagination-wrapper{width:100%;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}md-tabs-wrapper.md-stretch-tabs md-pagination-wrapper md-tab-item{-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}md-tabs-canvas{position:relative;overflow:hidden;display:block;height:48px}md-tabs-canvas:after{content:'';display:table;clear:both}md-tabs-canvas .md-dummy-wrapper{position:absolute;top:0;left:0}[dir=rtl] md-tabs-canvas .md-dummy-wrapper{left:0;left:auto;left:initial;right:0}md-tabs-canvas.md-paginated{margin:0 32px}md-tabs-canvas.md-center-tabs{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;text-align:center}md-tabs-canvas.md-center-tabs .md-tab{float:none;display:inline-block}md-pagination-wrapper{height:48px;display:block;transition:-webkit-transform .5s cubic-bezier(.35,0,.25,1);transition:transform .5s cubic-bezier(.35,0,.25,1);position:absolute;width:999999px;left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-pagination-wrapper:after{content:'';display:table;clear:both}[dir=rtl] md-pagination-wrapper{left:0;left:auto;left:initial;right:0}md-pagination-wrapper.md-center-tabs{position:relative;width:initial;margin:0 auto}md-tabs-content-wrapper{display:block;position:absolute;top:48px;left:0;right:0;bottom:0;overflow:hidden}md-tab-content{display:block;position:absolute;top:0;left:0;right:0;bottom:0;transition:-webkit-transform .5s cubic-bezier(.35,0,.25,1);transition:transform .5s cubic-bezier(.35,0,.25,1);overflow:auto;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-tab-content.md-no-scroll{bottom:auto;overflow:hidden}md-tab-content.md-no-transition,md-tab-content.ng-leave{transition:none}md-tab-content.md-left:not(.md-active){-webkit-transform:translateX(-100%);transform:translateX(-100%);-webkit-animation:1s md-tab-content-hide;animation:1s md-tab-content-hide;opacity:0}[dir=rtl] md-tab-content.md-left:not(.md-active){-webkit-transform:translateX(100%);transform:translateX(100%)}md-tab-content.md-left:not(.md-active) *{transition:visibility 0s linear;transition-delay:.5s;visibility:hidden}md-tab-content.md-right:not(.md-active){-webkit-transform:translateX(100%);transform:translateX(100%);-webkit-animation:1s md-tab-content-hide;animation:1s md-tab-content-hide;opacity:0}[dir=rtl] md-tab-content.md-right:not(.md-active){-webkit-transform:translateX(-100%);transform:translateX(-100%)}md-tab-content.md-right:not(.md-active) *{transition:visibility 0s linear;transition-delay:.5s;visibility:hidden}md-tab-content>div.ng-leave{-webkit-animation:1s md-tab-content-hide;animation:1s md-tab-content-hide}md-ink-bar{position:absolute;left:auto;right:auto;bottom:0;height:2px}md-ink-bar.md-left{transition:left .125s cubic-bezier(.35,0,.25,1),right .25s cubic-bezier(.35,0,.25,1)}md-ink-bar.md-right{transition:left .25s cubic-bezier(.35,0,.25,1),right .125s cubic-bezier(.35,0,.25,1)}md-tab{position:absolute;z-index:-1;left:-9999px}.md-tab{font-size:14px;text-align:center;line-height:24px;padding:12px 24px;transition:background-color .35s cubic-bezier(.35,0,.25,1);cursor:pointer;white-space:nowrap;position:relative;text-transform:uppercase;float:left;font-weight:500;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis}[dir=rtl] .md-tab{float:right}.md-tab.md-focused{box-shadow:none;outline:0}.md-tab.md-active{cursor:default}.md-tab.md-disabled{pointer-events:none;-ms-touch-action:pan-y;touch-action:pan-y;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none;opacity:.5;cursor:default}.md-tab.ng-leave{transition:none}md-toolbar+md-tabs{border-top-left-radius:0;border-top-right-radius:0}.md-toast-text{padding:0 6px}md-toast{position:absolute;z-index:105;box-sizing:border-box;cursor:default;overflow:hidden;padding:8px;opacity:1;transition:all .4s cubic-bezier(.25,.8,.25,1)}md-toast .md-toast-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;max-height:168px;max-width:100%;min-height:48px;padding:0 18px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);border-radius:2px;font-size:14px;overflow:hidden;-webkit-transform:translate3d(0,0,0) rotateZ(0deg);transform:translate3d(0,0,0) rotateZ(0deg);transition:all .4s cubic-bezier(.25,.8,.25,1);-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}md-toast .md-toast-content::before{content:'';min-height:48px;visibility:hidden;display:inline-block}[dir=rtl] md-toast .md-toast-content{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}md-toast.md-capsule,md-toast.md-capsule .md-toast-content{border-radius:24px}md-toast.ng-leave-active .md-toast-content{transition:all .3s cubic-bezier(.55,0,.55,.2)}md-toast._md-swipedown .md-toast-content,md-toast._md-swipeleft .md-toast-content,md-toast._md-swiperight .md-toast-content,md-toast._md-swipeup .md-toast-content{transition:all .4s cubic-bezier(.25,.8,.25,1)}md-toast.ng-enter{opacity:0}md-toast.ng-enter .md-toast-content{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}md-toast.ng-enter._md-top .md-toast-content{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}md-toast.ng-enter.ng-enter-active{opacity:1}md-toast.ng-enter.ng-enter-active .md-toast-content{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}md-toast.ng-leave.ng-leave-active .md-toast-content{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}md-toast.ng-leave.ng-leave-active._md-swipeup .md-toast-content{-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0)}md-toast.ng-leave.ng-leave-active._md-swipedown .md-toast-content{-webkit-transform:translate3d(0,50%,0);transform:translate3d(0,50%,0)}md-toast.ng-leave.ng-leave-active._md-top .md-toast-content{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}md-toast .md-action{line-height:19px;margin-left:24px;margin-right:0;cursor:pointer;text-transform:uppercase;float:right}md-toast .md-button{min-width:0;margin-right:0;margin-left:12px}[dir=rtl] md-toast .md-button{margin-right:12px;margin-left:0}@media (max-width:959px){md-toast{left:0;right:0;width:100%;max-width:100%;min-width:0;border-radius:0;bottom:0;padding:0}md-toast.ng-leave.ng-leave-active._md-swipeup .md-toast-content{-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0)}md-toast.ng-leave.ng-leave-active._md-swipedown .md-toast-content{-webkit-transform:translate3d(0,50%,0);transform:translate3d(0,50%,0)}}@media (min-width:960px){md-toast{min-width:304px}md-toast._md-bottom{bottom:0}md-toast._md-left{left:0}md-toast._md-right{right:0}md-toast._md-top{top:0}md-toast._md-start{left:0}[dir=rtl] md-toast._md-start{left:0;left:auto;left:initial;right:0}md-toast._md-end{right:0}[dir=rtl] md-toast._md-end{right:0;right:auto;right:initial;left:0}md-toast.ng-leave.ng-leave-active._md-swipeleft .md-toast-content{-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}md-toast.ng-leave.ng-leave-active._md-swiperight .md-toast-content{-webkit-transform:translate3d(50%,0,0);transform:translate3d(50%,0,0)}}@media (min-width:1920px){md-toast .md-toast-content{max-width:568px}}@media screen and (-ms-high-contrast:active){md-toast{border:1px solid #fff}}._md-toast-animating{overflow:hidden!important}md-tooltip{position:absolute;z-index:100;overflow:hidden;pointer-events:none;border-radius:4px;font-weight:500;font-size:14px}@media (min-width:960px){md-tooltip{font-size:10px}}md-tooltip ._md-content{position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-transform-origin:center top;transform-origin:center top;-webkit-transform:scale(0);transform:scale(0);opacity:0;height:32px;line-height:32px;padding-left:16px;padding-right:16px}@media (min-width:960px){md-tooltip ._md-content{height:22px;line-height:22px;padding-left:8px;padding-right:8px}}md-tooltip ._md-content._md-show-add{transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:.2s;-webkit-transform:scale(0);transform:scale(0);opacity:0}md-tooltip ._md-content._md-show,md-tooltip ._md-content._md-show-add-active{-webkit-transform:scale(1);transform:scale(1);opacity:1;-webkit-transform-origin:center top;transform-origin:center top}md-tooltip ._md-content._md-show-remove{transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:.2s}md-tooltip ._md-content._md-show-remove._md-show-remove-active{-webkit-transform:scale(0);transform:scale(0);opacity:0}md-tooltip._md-hide{transition:all .3s cubic-bezier(.55,0,.55,.2)}md-tooltip._md-show{transition:all .4s cubic-bezier(.25,.8,.25,1);pointer-events:auto}.md-virtual-repeat-container{box-sizing:border-box;display:block;margin:0;overflow:hidden;padding:0;position:relative}.md-virtual-repeat-container .md-virtual-repeat-scroller{bottom:0;box-sizing:border-box;left:0;margin:0;overflow-x:hidden;padding:0;position:absolute;right:0;top:0}.md-virtual-repeat-container .md-virtual-repeat-sizer{box-sizing:border-box;height:1px;display:block;margin:0;padding:0;width:1px}.md-virtual-repeat-container .md-virtual-repeat-offsetter{box-sizing:border-box;left:0;margin:0;padding:0;position:absolute;right:0;top:0}.md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-scroller{overflow-x:auto;overflow-y:hidden}.md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-offsetter{bottom:16px;right:auto;white-space:nowrap}[dir=rtl] .md-virtual-repeat-container.md-orient-horizontal .md-virtual-repeat-offsetter{right:0;right:auto;right:initial;left:auto}md-toolbar{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;position:relative;z-index:2;font-size:20px;min-height:64px;width:100%;transition-duration:.5s;transition-timing-function:cubic-bezier(.35,0,.25,1);transition-property:background-color,fill,color}md-toolbar.md-whiteframe-z1-add,md-toolbar.md-whiteframe-z1-remove{transition:box-shadow .5s linear}md-toolbar md-toolbar-filler{width:72px}md-toolbar *,md-toolbar :after,md-toolbar :before{box-sizing:border-box}md-toolbar.ng-animate{transition:none}md-toolbar.md-tall{height:128px;min-height:128px;max-height:128px}md-toolbar.md-medium-tall{height:88px;min-height:88px;max-height:88px}md-toolbar.md-medium-tall .md-toolbar-tools{height:48px;min-height:48px;max-height:48px}md-toolbar>.md-indent{margin-left:64px}[dir=rtl] md-toolbar>.md-indent{margin-left:auto;margin-left:initial;margin-right:64px}md-toolbar~md-content>md-list{padding:0}md-toolbar~md-content>md-list md-list-item:last-child md-divider{display:none}.md-toolbar-tools{font-size:20px;letter-spacing:.005em;box-sizing:border-box;font-weight:400;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;width:100%;height:64px;max-height:64px;padding:0 16px;margin:0}.md-toolbar-tools h1,.md-toolbar-tools h2,.md-toolbar-tools h3{font-size:inherit;font-weight:inherit;margin:inherit}.md-toolbar-tools a{color:inherit;text-decoration:none}.md-toolbar-tools .fill-height{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.md-toolbar-tools .md-button{margin-top:0;margin-bottom:0}.md-toolbar-tools .md-button,.md-toolbar-tools .md-button.md-icon-button md-icon{transition-duration:.5s;transition-timing-function:cubic-bezier(.35,0,.25,1);transition-property:background-color,fill,color}.md-toolbar-tools .md-button.md-icon-button md-icon.ng-animate,.md-toolbar-tools .md-button.ng-animate{transition:none}.md-toolbar-tools>.md-button:first-child{margin-left:-8px}[dir=rtl] .md-toolbar-tools>.md-button:first-child{margin-left:auto;margin-left:initial;margin-right:-8px}.md-toolbar-tools>.md-button:last-child{margin-right:-8px}[dir=rtl] .md-toolbar-tools>.md-button:last-child{margin-right:auto;margin-right:initial;margin-left:-8px}.md-toolbar-tools>md-menu:last-child{margin-right:-8px}[dir=rtl] .md-toolbar-tools>md-menu:last-child{margin-right:auto;margin-right:initial;margin-left:-8px}.md-toolbar-tools>md-menu:last-child>.md-button{margin-right:0}[dir=rtl] .md-toolbar-tools>md-menu:last-child>.md-button{margin-right:auto;margin-right:initial;margin-left:0}@media screen and (-ms-high-contrast:active){.md-toolbar-tools{border-bottom:1px solid #fff}}@media (min-width:0) and (max-width:959px) and (orientation:portrait){md-toolbar{min-height:56px}.md-toolbar-tools{height:56px;max-height:56px}}@media (min-width:0) and (max-width:959px) and (orientation:landscape){md-toolbar{min-height:48px}.md-toolbar-tools{height:48px;max-height:48px}}.md-whiteframe-1dp,.md-whiteframe-z1{box-shadow:0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)}.md-whiteframe-2dp{box-shadow:0 1px 5px 0 rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12)}.md-whiteframe-3dp{box-shadow:0 1px 8px 0 rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.12)}.md-whiteframe-4dp,.md-whiteframe-z2{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.md-whiteframe-5dp{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12)}.md-whiteframe-6dp{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)}.md-whiteframe-7dp,.md-whiteframe-z3{box-shadow:0 4px 5px -2px rgba(0,0,0,.2),0 7px 10px 1px rgba(0,0,0,.14),0 2px 16px 1px rgba(0,0,0,.12)}.md-whiteframe-8dp{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.md-whiteframe-9dp{box-shadow:0 5px 6px -3px rgba(0,0,0,.2),0 9px 12px 1px rgba(0,0,0,.14),0 3px 16px 2px rgba(0,0,0,.12)}.md-whiteframe-10dp,.md-whiteframe-z4{box-shadow:0 6px 6px -3px rgba(0,0,0,.2),0 10px 14px 1px rgba(0,0,0,.14),0 4px 18px 3px rgba(0,0,0,.12)}.md-whiteframe-11dp{box-shadow:0 6px 7px -4px rgba(0,0,0,.2),0 11px 15px 1px rgba(0,0,0,.14),0 4px 20px 3px rgba(0,0,0,.12)}.md-whiteframe-12dp{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)}.md-whiteframe-13dp,.md-whiteframe-z5{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12)}.md-whiteframe-14dp{box-shadow:0 7px 9px -4px rgba(0,0,0,.2),0 14px 21px 2px rgba(0,0,0,.14),0 5px 26px 4px rgba(0,0,0,.12)}.md-whiteframe-15dp{box-shadow:0 8px 9px -5px rgba(0,0,0,.2),0 15px 22px 2px rgba(0,0,0,.14),0 6px 28px 5px rgba(0,0,0,.12)}.md-whiteframe-16dp{box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.md-whiteframe-17dp{box-shadow:0 8px 11px -5px rgba(0,0,0,.2),0 17px 26px 2px rgba(0,0,0,.14),0 6px 32px 5px rgba(0,0,0,.12)}.md-whiteframe-18dp{box-shadow:0 9px 11px -5px rgba(0,0,0,.2),0 18px 28px 2px rgba(0,0,0,.14),0 7px 34px 6px rgba(0,0,0,.12)}.md-whiteframe-19dp{box-shadow:0 9px 12px -6px rgba(0,0,0,.2),0 19px 29px 2px rgba(0,0,0,.14),0 7px 36px 6px rgba(0,0,0,.12)}.md-whiteframe-20dp{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 20px 31px 3px rgba(0,0,0,.14),0 8px 38px 7px rgba(0,0,0,.12)}.md-whiteframe-21dp{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 21px 33px 3px rgba(0,0,0,.14),0 8px 40px 7px rgba(0,0,0,.12)}.md-whiteframe-22dp{box-shadow:0 10px 14px -6px rgba(0,0,0,.2),0 22px 35px 3px rgba(0,0,0,.14),0 8px 42px 7px rgba(0,0,0,.12)}.md-whiteframe-23dp{box-shadow:0 11px 14px -7px rgba(0,0,0,.2),0 23px 36px 3px rgba(0,0,0,.14),0 9px 44px 8px rgba(0,0,0,.12)}.md-whiteframe-24dp{box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}@media screen and (-ms-high-contrast:active){md-whiteframe{border:1px solid #fff}}@media print{[md-whiteframe],md-whiteframe{background-color:#fff}}.ng-cloak,.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}@-moz-document url-prefix(){.layout-fill{margin:0;width:100%;min-height:100%;height:100%}}.flex-order{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-0,.offset-0{margin-left:0}[dir=rtl] .flex-offset-0,[dir=rtl] .offset-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-5,.offset-5{margin-left:5%}[dir=rtl] .flex-offset-5,[dir=rtl] .offset-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-10,.offset-10{margin-left:10%}[dir=rtl] .flex-offset-10,[dir=rtl] .offset-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-15,.offset-15{margin-left:15%}[dir=rtl] .flex-offset-15,[dir=rtl] .offset-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-20,.offset-20{margin-left:20%}[dir=rtl] .flex-offset-20,[dir=rtl] .offset-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-25,.offset-25{margin-left:25%}[dir=rtl] .flex-offset-25,[dir=rtl] .offset-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-30,.offset-30{margin-left:30%}[dir=rtl] .flex-offset-30,[dir=rtl] .offset-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-35,.offset-35{margin-left:35%}[dir=rtl] .flex-offset-35,[dir=rtl] .offset-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-40,.offset-40{margin-left:40%}[dir=rtl] .flex-offset-40,[dir=rtl] .offset-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-45,.offset-45{margin-left:45%}[dir=rtl] .flex-offset-45,[dir=rtl] .offset-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-50,.offset-50{margin-left:50%}[dir=rtl] .flex-offset-50,[dir=rtl] .offset-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-55,.offset-55{margin-left:55%}[dir=rtl] .flex-offset-55,[dir=rtl] .offset-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-60,.offset-60{margin-left:60%}[dir=rtl] .flex-offset-60,[dir=rtl] .offset-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-65,.offset-65{margin-left:65%}[dir=rtl] .flex-offset-65,[dir=rtl] .offset-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-70,.offset-70{margin-left:70%}[dir=rtl] .flex-offset-70,[dir=rtl] .offset-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-75,.offset-75{margin-left:75%}[dir=rtl] .flex-offset-75,[dir=rtl] .offset-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-80,.offset-80{margin-left:80%}[dir=rtl] .flex-offset-80,[dir=rtl] .offset-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-85,.offset-85{margin-left:85%}[dir=rtl] .flex-offset-85,[dir=rtl] .offset-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-90,.offset-90{margin-left:90%}[dir=rtl] .flex-offset-90,[dir=rtl] .offset-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-95,.offset-95{margin-left:95%}[dir=rtl] .flex-offset-95,[dir=rtl] .offset-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-33,.offset-33{margin-left:calc(100% / 3)}.flex-offset-66,.offset-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-66,[dir=rtl] .offset-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-start,.layout-align-start-center,.layout-align-start-end,.layout-align-start-start,.layout-align-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-center,.layout-align-center-center,.layout-align-center-end,.layout-align-center-start,.layout-align-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-end,.layout-align-end-center,.layout-align-end-end,.layout-align-end-start,.layout-align-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-space-around,.layout-align-space-around-center,.layout-align-space-around-end,.layout-align-space-around-start,.layout-align-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-space-between,.layout-align-space-between-center,.layout-align-space-between-end,.layout-align-space-between-start,.layout-align-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-center-start,.layout-align-end-start,.layout-align-space-around-start,.layout-align-space-between-start,.layout-align-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-center-center,.layout-align-end-center,.layout-align-space-around-center,.layout-align-space-between-center,.layout-align-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-center-center>*,.layout-align-end-center>*,.layout-align-space-around-center>*,.layout-align-space-between-center>*,.layout-align-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-center-end,.layout-align-end-end,.layout-align-space-around-end,.layout-align-space-between-end,.layout-align-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-center-stretch,.layout-align-end-stretch,.layout-align-space-around-stretch,.layout-align-space-between-stretch,.layout-align-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-row>.flex-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-5,.layout-row>.flex-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-10,.layout-row>.flex-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-15,.layout-row>.flex-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-20,.layout-row>.flex-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-25,.layout-row>.flex-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-30,.layout-row>.flex-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-35,.layout-row>.flex-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-40,.layout-row>.flex-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-45,.layout-row>.flex-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-50,.layout-row>.flex-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-55,.layout-row>.flex-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-60,.layout-row>.flex-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-65,.layout-row>.flex-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-70,.layout-row>.flex-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-75,.layout-row>.flex-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-80,.layout-row>.flex-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-85,.layout-row>.flex-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-90,.layout-row>.flex-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-95,.layout-row>.flex-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-100,.layout-column>.flex-100,.layout-row>.flex-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-row>.flex-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-row>.flex-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-row>.flex{min-width:0}.layout-column>.flex-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex{min-height:0}.layout,.layout-column,.layout-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.layout-padding-sm>*,.layout-padding>.flex-sm{padding:4px}.layout-padding,.layout-padding-gt-sm,.layout-padding-gt-sm>*,.layout-padding-md,.layout-padding-md>*,.layout-padding>*,.layout-padding>.flex,.layout-padding>.flex-gt-sm,.layout-padding>.flex-md{padding:8px}.layout-padding-gt-lg>*,.layout-padding-gt-md>*,.layout-padding-lg>*,.layout-padding>.flex-gt-lg,.layout-padding>.flex-gt-md,.layout-padding>.flex-lg{padding:16px}.layout-margin-sm>*,.layout-margin>.flex-sm{margin:4px}.layout-margin,.layout-margin-gt-sm,.layout-margin-gt-sm>*,.layout-margin-md,.layout-margin-md>*,.layout-margin>*,.layout-margin>.flex,.layout-margin>.flex-gt-sm,.layout-margin>.flex-md{margin:8px}.layout-margin-gt-lg>*,.layout-margin-gt-md>*,.layout-margin-lg>*,.layout-margin>.flex-gt-lg,.layout-margin>.flex-gt-md,.layout-margin>.flex-lg{margin:16px}.layout-wrap{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.layout-nowrap{-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.layout-fill{margin:0;width:100%;min-height:100%;height:100%}@media (max-width:599px){.hide-xs:not(.show-xs):not(.show),.hide:not(.show-xs):not(.show){display:none}.flex-order-xs--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-xs--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-xs--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-xs--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-xs--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-xs--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-xs--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-xs--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-xs--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-xs--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-xs--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-xs--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-xs--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-xs--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-xs--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-xs--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-xs--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-xs--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-xs--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-xs--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-xs-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-xs-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-xs-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-xs-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-xs-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-xs-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-xs-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-xs-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-xs-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-xs-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-xs-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-xs-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-xs-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-xs-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-xs-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-xs-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-xs-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-xs-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-xs-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-xs-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-xs-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-xs-0,.offset-xs-0{margin-left:0}[dir=rtl] .flex-offset-xs-0,[dir=rtl] .offset-xs-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-xs-5,.offset-xs-5{margin-left:5%}[dir=rtl] .flex-offset-xs-5,[dir=rtl] .offset-xs-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-xs-10,.offset-xs-10{margin-left:10%}[dir=rtl] .flex-offset-xs-10,[dir=rtl] .offset-xs-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-xs-15,.offset-xs-15{margin-left:15%}[dir=rtl] .flex-offset-xs-15,[dir=rtl] .offset-xs-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-xs-20,.offset-xs-20{margin-left:20%}[dir=rtl] .flex-offset-xs-20,[dir=rtl] .offset-xs-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-xs-25,.offset-xs-25{margin-left:25%}[dir=rtl] .flex-offset-xs-25,[dir=rtl] .offset-xs-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-xs-30,.offset-xs-30{margin-left:30%}[dir=rtl] .flex-offset-xs-30,[dir=rtl] .offset-xs-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-xs-35,.offset-xs-35{margin-left:35%}[dir=rtl] .flex-offset-xs-35,[dir=rtl] .offset-xs-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-xs-40,.offset-xs-40{margin-left:40%}[dir=rtl] .flex-offset-xs-40,[dir=rtl] .offset-xs-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-xs-45,.offset-xs-45{margin-left:45%}[dir=rtl] .flex-offset-xs-45,[dir=rtl] .offset-xs-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-xs-50,.offset-xs-50{margin-left:50%}[dir=rtl] .flex-offset-xs-50,[dir=rtl] .offset-xs-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-xs-55,.offset-xs-55{margin-left:55%}[dir=rtl] .flex-offset-xs-55,[dir=rtl] .offset-xs-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-xs-60,.offset-xs-60{margin-left:60%}[dir=rtl] .flex-offset-xs-60,[dir=rtl] .offset-xs-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-xs-65,.offset-xs-65{margin-left:65%}[dir=rtl] .flex-offset-xs-65,[dir=rtl] .offset-xs-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-xs-70,.offset-xs-70{margin-left:70%}[dir=rtl] .flex-offset-xs-70,[dir=rtl] .offset-xs-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-xs-75,.offset-xs-75{margin-left:75%}[dir=rtl] .flex-offset-xs-75,[dir=rtl] .offset-xs-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-xs-80,.offset-xs-80{margin-left:80%}[dir=rtl] .flex-offset-xs-80,[dir=rtl] .offset-xs-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-xs-85,.offset-xs-85{margin-left:85%}[dir=rtl] .flex-offset-xs-85,[dir=rtl] .offset-xs-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-xs-90,.offset-xs-90{margin-left:90%}[dir=rtl] .flex-offset-xs-90,[dir=rtl] .offset-xs-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-xs-95,.offset-xs-95{margin-left:95%}[dir=rtl] .flex-offset-xs-95,[dir=rtl] .offset-xs-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-xs-33,.offset-xs-33{margin-left:calc(100% / 3)}.flex-offset-xs-66,.offset-xs-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-xs-66,[dir=rtl] .offset-xs-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-xs{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-xs-start,.layout-align-xs-start-center,.layout-align-xs-start-end,.layout-align-xs-start-start,.layout-align-xs-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-xs-center,.layout-align-xs-center-center,.layout-align-xs-center-end,.layout-align-xs-center-start,.layout-align-xs-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-xs-end,.layout-align-xs-end-center,.layout-align-xs-end-end,.layout-align-xs-end-start,.layout-align-xs-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-xs-space-around,.layout-align-xs-space-around-center,.layout-align-xs-space-around-end,.layout-align-xs-space-around-start,.layout-align-xs-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-xs-space-between,.layout-align-xs-space-between-center,.layout-align-xs-space-between-end,.layout-align-xs-space-between-start,.layout-align-xs-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-xs-center-start,.layout-align-xs-end-start,.layout-align-xs-space-around-start,.layout-align-xs-space-between-start,.layout-align-xs-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-xs-center-center,.layout-align-xs-end-center,.layout-align-xs-space-around-center,.layout-align-xs-space-between-center,.layout-align-xs-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-xs-center-center>*,.layout-align-xs-end-center>*,.layout-align-xs-space-around-center>*,.layout-align-xs-space-between-center>*,.layout-align-xs-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-xs-center-end,.layout-align-xs-end-end,.layout-align-xs-space-around-end,.layout-align-xs-space-between-end,.layout-align-xs-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-xs-center-stretch,.layout-align-xs-end-stretch,.layout-align-xs-space-around-stretch,.layout-align-xs-space-between-stretch,.layout-align-xs-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-xs{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-xs-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-xs-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-xs-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-xs-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-xs-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-xs-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-row>.flex-xs-0,.layout-xs-row>.flex-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-xs-0,.layout-xs-column>.flex-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-xs-5,.layout-row>.flex-xs-5,.layout-xs-row>.flex-xs-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-5,.layout-xs-column>.flex-xs-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-xs-10,.layout-row>.flex-xs-10,.layout-xs-row>.flex-xs-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-10,.layout-xs-column>.flex-xs-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-xs-15,.layout-row>.flex-xs-15,.layout-xs-row>.flex-xs-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-15,.layout-xs-column>.flex-xs-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-xs-20,.layout-row>.flex-xs-20,.layout-xs-row>.flex-xs-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-20,.layout-xs-column>.flex-xs-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-xs-25,.layout-row>.flex-xs-25,.layout-xs-row>.flex-xs-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-25,.layout-xs-column>.flex-xs-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-xs-30,.layout-row>.flex-xs-30,.layout-xs-row>.flex-xs-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-30,.layout-xs-column>.flex-xs-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-xs-35,.layout-row>.flex-xs-35,.layout-xs-row>.flex-xs-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-35,.layout-xs-column>.flex-xs-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-xs-40,.layout-row>.flex-xs-40,.layout-xs-row>.flex-xs-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-40,.layout-xs-column>.flex-xs-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-xs-45,.layout-row>.flex-xs-45,.layout-xs-row>.flex-xs-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-45,.layout-xs-column>.flex-xs-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-xs-50,.layout-row>.flex-xs-50,.layout-xs-row>.flex-xs-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-50,.layout-xs-column>.flex-xs-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-xs-55,.layout-row>.flex-xs-55,.layout-xs-row>.flex-xs-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-55,.layout-xs-column>.flex-xs-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-xs-60,.layout-row>.flex-xs-60,.layout-xs-row>.flex-xs-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-60,.layout-xs-column>.flex-xs-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-xs-65,.layout-row>.flex-xs-65,.layout-xs-row>.flex-xs-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-65,.layout-xs-column>.flex-xs-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-xs-70,.layout-row>.flex-xs-70,.layout-xs-row>.flex-xs-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-70,.layout-xs-column>.flex-xs-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-xs-75,.layout-row>.flex-xs-75,.layout-xs-row>.flex-xs-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-75,.layout-xs-column>.flex-xs-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-xs-80,.layout-row>.flex-xs-80,.layout-xs-row>.flex-xs-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-80,.layout-xs-column>.flex-xs-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-xs-85,.layout-row>.flex-xs-85,.layout-xs-row>.flex-xs-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-85,.layout-xs-column>.flex-xs-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-xs-90,.layout-row>.flex-xs-90,.layout-xs-row>.flex-xs-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-90,.layout-xs-column>.flex-xs-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-xs-95,.layout-row>.flex-xs-95,.layout-xs-row>.flex-xs-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xs-95,.layout-xs-column>.flex-xs-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-xs-100,.layout-column>.flex-xs-100,.layout-row>.flex-xs-100,.layout-xs-column>.flex-xs-100,.layout-xs-row>.flex-xs-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-row>.flex-xs-33,.layout-xs-row>.flex-xs-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-row>.flex-xs-66,.layout-xs-row>.flex-xs-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-row>.flex,.layout-xs-row>.flex{min-width:0}.layout-column>.flex-xs-33,.layout-xs-column>.flex-xs-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-xs-66,.layout-xs-column>.flex-xs-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-xs-column>.flex{min-height:0}.layout-xs,.layout-xs-column,.layout-xs-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-xs-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-xs-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:600px){.flex-order-gt-xs--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-gt-xs--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-gt-xs--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-gt-xs--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-gt-xs--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-gt-xs--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-gt-xs--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-gt-xs--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-gt-xs--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-gt-xs--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-gt-xs--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-gt-xs--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-gt-xs--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-gt-xs--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-gt-xs--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-gt-xs--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-gt-xs--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-gt-xs--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-gt-xs--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-gt-xs--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-gt-xs-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-gt-xs-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-gt-xs-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-gt-xs-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-gt-xs-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-gt-xs-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-gt-xs-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-gt-xs-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-gt-xs-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-gt-xs-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-gt-xs-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-gt-xs-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-gt-xs-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-gt-xs-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-gt-xs-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-gt-xs-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-gt-xs-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-gt-xs-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-gt-xs-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-gt-xs-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-gt-xs-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-gt-xs-0,.offset-gt-xs-0{margin-left:0}[dir=rtl] .flex-offset-gt-xs-0,[dir=rtl] .offset-gt-xs-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-gt-xs-5,.offset-gt-xs-5{margin-left:5%}[dir=rtl] .flex-offset-gt-xs-5,[dir=rtl] .offset-gt-xs-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-gt-xs-10,.offset-gt-xs-10{margin-left:10%}[dir=rtl] .flex-offset-gt-xs-10,[dir=rtl] .offset-gt-xs-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-gt-xs-15,.offset-gt-xs-15{margin-left:15%}[dir=rtl] .flex-offset-gt-xs-15,[dir=rtl] .offset-gt-xs-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-gt-xs-20,.offset-gt-xs-20{margin-left:20%}[dir=rtl] .flex-offset-gt-xs-20,[dir=rtl] .offset-gt-xs-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-gt-xs-25,.offset-gt-xs-25{margin-left:25%}[dir=rtl] .flex-offset-gt-xs-25,[dir=rtl] .offset-gt-xs-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-gt-xs-30,.offset-gt-xs-30{margin-left:30%}[dir=rtl] .flex-offset-gt-xs-30,[dir=rtl] .offset-gt-xs-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-gt-xs-35,.offset-gt-xs-35{margin-left:35%}[dir=rtl] .flex-offset-gt-xs-35,[dir=rtl] .offset-gt-xs-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-gt-xs-40,.offset-gt-xs-40{margin-left:40%}[dir=rtl] .flex-offset-gt-xs-40,[dir=rtl] .offset-gt-xs-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-gt-xs-45,.offset-gt-xs-45{margin-left:45%}[dir=rtl] .flex-offset-gt-xs-45,[dir=rtl] .offset-gt-xs-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-gt-xs-50,.offset-gt-xs-50{margin-left:50%}[dir=rtl] .flex-offset-gt-xs-50,[dir=rtl] .offset-gt-xs-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-gt-xs-55,.offset-gt-xs-55{margin-left:55%}[dir=rtl] .flex-offset-gt-xs-55,[dir=rtl] .offset-gt-xs-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-gt-xs-60,.offset-gt-xs-60{margin-left:60%}[dir=rtl] .flex-offset-gt-xs-60,[dir=rtl] .offset-gt-xs-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-gt-xs-65,.offset-gt-xs-65{margin-left:65%}[dir=rtl] .flex-offset-gt-xs-65,[dir=rtl] .offset-gt-xs-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-gt-xs-70,.offset-gt-xs-70{margin-left:70%}[dir=rtl] .flex-offset-gt-xs-70,[dir=rtl] .offset-gt-xs-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-gt-xs-75,.offset-gt-xs-75{margin-left:75%}[dir=rtl] .flex-offset-gt-xs-75,[dir=rtl] .offset-gt-xs-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-gt-xs-80,.offset-gt-xs-80{margin-left:80%}[dir=rtl] .flex-offset-gt-xs-80,[dir=rtl] .offset-gt-xs-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-gt-xs-85,.offset-gt-xs-85{margin-left:85%}[dir=rtl] .flex-offset-gt-xs-85,[dir=rtl] .offset-gt-xs-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-gt-xs-90,.offset-gt-xs-90{margin-left:90%}[dir=rtl] .flex-offset-gt-xs-90,[dir=rtl] .offset-gt-xs-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-gt-xs-95,.offset-gt-xs-95{margin-left:95%}[dir=rtl] .flex-offset-gt-xs-95,[dir=rtl] .offset-gt-xs-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-gt-xs-33,.offset-gt-xs-33{margin-left:calc(100% / 3)}.flex-offset-gt-xs-66,.offset-gt-xs-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-gt-xs-66,[dir=rtl] .offset-gt-xs-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-gt-xs{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-gt-xs-start,.layout-align-gt-xs-start-center,.layout-align-gt-xs-start-end,.layout-align-gt-xs-start-start,.layout-align-gt-xs-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-gt-xs-center,.layout-align-gt-xs-center-center,.layout-align-gt-xs-center-end,.layout-align-gt-xs-center-start,.layout-align-gt-xs-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-gt-xs-end,.layout-align-gt-xs-end-center,.layout-align-gt-xs-end-end,.layout-align-gt-xs-end-start,.layout-align-gt-xs-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-gt-xs-space-around,.layout-align-gt-xs-space-around-center,.layout-align-gt-xs-space-around-end,.layout-align-gt-xs-space-around-start,.layout-align-gt-xs-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-gt-xs-space-between,.layout-align-gt-xs-space-between-center,.layout-align-gt-xs-space-between-end,.layout-align-gt-xs-space-between-start,.layout-align-gt-xs-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-gt-xs-center-start,.layout-align-gt-xs-end-start,.layout-align-gt-xs-space-around-start,.layout-align-gt-xs-space-between-start,.layout-align-gt-xs-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-gt-xs-center-center,.layout-align-gt-xs-end-center,.layout-align-gt-xs-space-around-center,.layout-align-gt-xs-space-between-center,.layout-align-gt-xs-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-gt-xs-center-center>*,.layout-align-gt-xs-end-center>*,.layout-align-gt-xs-space-around-center>*,.layout-align-gt-xs-space-between-center>*,.layout-align-gt-xs-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-gt-xs-center-end,.layout-align-gt-xs-end-end,.layout-align-gt-xs-space-around-end,.layout-align-gt-xs-space-between-end,.layout-align-gt-xs-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-gt-xs-center-stretch,.layout-align-gt-xs-end-stretch,.layout-align-gt-xs-space-around-stretch,.layout-align-gt-xs-space-between-stretch,.layout-align-gt-xs-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-gt-xs{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-gt-xs-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-gt-xs-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-xs-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-gt-xs-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-gt-xs-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-gt-xs-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-gt-xs-row>.flex-gt-xs-0,.layout-row>.flex-gt-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-gt-xs-0,.layout-gt-xs-column>.flex-gt-xs-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-gt-xs-5,.layout-gt-xs-row>.flex-gt-xs-5,.layout-row>.flex-gt-xs-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-5,.layout-gt-xs-column>.flex-gt-xs-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-gt-xs-10,.layout-gt-xs-row>.flex-gt-xs-10,.layout-row>.flex-gt-xs-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-10,.layout-gt-xs-column>.flex-gt-xs-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-gt-xs-15,.layout-gt-xs-row>.flex-gt-xs-15,.layout-row>.flex-gt-xs-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-15,.layout-gt-xs-column>.flex-gt-xs-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-gt-xs-20,.layout-gt-xs-row>.flex-gt-xs-20,.layout-row>.flex-gt-xs-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-20,.layout-gt-xs-column>.flex-gt-xs-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-gt-xs-25,.layout-gt-xs-row>.flex-gt-xs-25,.layout-row>.flex-gt-xs-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-25,.layout-gt-xs-column>.flex-gt-xs-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-gt-xs-30,.layout-gt-xs-row>.flex-gt-xs-30,.layout-row>.flex-gt-xs-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-30,.layout-gt-xs-column>.flex-gt-xs-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-gt-xs-35,.layout-gt-xs-row>.flex-gt-xs-35,.layout-row>.flex-gt-xs-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-35,.layout-gt-xs-column>.flex-gt-xs-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-gt-xs-40,.layout-gt-xs-row>.flex-gt-xs-40,.layout-row>.flex-gt-xs-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-40,.layout-gt-xs-column>.flex-gt-xs-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-gt-xs-45,.layout-gt-xs-row>.flex-gt-xs-45,.layout-row>.flex-gt-xs-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-45,.layout-gt-xs-column>.flex-gt-xs-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-gt-xs-50,.layout-gt-xs-row>.flex-gt-xs-50,.layout-row>.flex-gt-xs-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-50,.layout-gt-xs-column>.flex-gt-xs-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-gt-xs-55,.layout-gt-xs-row>.flex-gt-xs-55,.layout-row>.flex-gt-xs-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-55,.layout-gt-xs-column>.flex-gt-xs-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-gt-xs-60,.layout-gt-xs-row>.flex-gt-xs-60,.layout-row>.flex-gt-xs-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-60,.layout-gt-xs-column>.flex-gt-xs-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-gt-xs-65,.layout-gt-xs-row>.flex-gt-xs-65,.layout-row>.flex-gt-xs-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-65,.layout-gt-xs-column>.flex-gt-xs-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-gt-xs-70,.layout-gt-xs-row>.flex-gt-xs-70,.layout-row>.flex-gt-xs-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-70,.layout-gt-xs-column>.flex-gt-xs-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-gt-xs-75,.layout-gt-xs-row>.flex-gt-xs-75,.layout-row>.flex-gt-xs-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-75,.layout-gt-xs-column>.flex-gt-xs-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-gt-xs-80,.layout-gt-xs-row>.flex-gt-xs-80,.layout-row>.flex-gt-xs-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-80,.layout-gt-xs-column>.flex-gt-xs-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-gt-xs-85,.layout-gt-xs-row>.flex-gt-xs-85,.layout-row>.flex-gt-xs-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-85,.layout-gt-xs-column>.flex-gt-xs-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-gt-xs-90,.layout-gt-xs-row>.flex-gt-xs-90,.layout-row>.flex-gt-xs-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-90,.layout-gt-xs-column>.flex-gt-xs-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-gt-xs-95,.layout-gt-xs-row>.flex-gt-xs-95,.layout-row>.flex-gt-xs-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-xs-95,.layout-gt-xs-column>.flex-gt-xs-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-gt-xs-100,.layout-column>.flex-gt-xs-100,.layout-gt-xs-column>.flex-gt-xs-100,.layout-gt-xs-row>.flex-gt-xs-100,.layout-row>.flex-gt-xs-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-gt-xs-row>.flex-gt-xs-33,.layout-row>.flex-gt-xs-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-gt-xs-row>.flex-gt-xs-66,.layout-row>.flex-gt-xs-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-gt-xs-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-gt-xs-33,.layout-gt-xs-column>.flex-gt-xs-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-gt-xs-66,.layout-gt-xs-column>.flex-gt-xs-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-gt-xs-column>.flex{min-height:0}.layout-gt-xs,.layout-gt-xs-column,.layout-gt-xs-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-gt-xs-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-gt-xs-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:600px) and (max-width:959px){.hide-gt-xs:not(.show-gt-xs):not(.show-sm):not(.show),.hide-sm:not(.show-gt-xs):not(.show-sm):not(.show),.hide:not(.show-gt-xs):not(.show-sm):not(.show){display:none}.flex-order-sm--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-sm--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-sm--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-sm--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-sm--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-sm--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-sm--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-sm--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-sm--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-sm--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-sm--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-sm--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-sm--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-sm--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-sm--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-sm--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-sm--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-sm--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-sm--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-sm--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-sm-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-sm-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-sm-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-sm-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-sm-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-sm-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-sm-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-sm-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-sm-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-sm-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-sm-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-sm-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-sm-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-sm-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-sm-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-sm-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-sm-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-sm-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-sm-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-sm-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-sm-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-sm-0,.offset-sm-0{margin-left:0}[dir=rtl] .flex-offset-sm-0,[dir=rtl] .offset-sm-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-sm-5,.offset-sm-5{margin-left:5%}[dir=rtl] .flex-offset-sm-5,[dir=rtl] .offset-sm-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-sm-10,.offset-sm-10{margin-left:10%}[dir=rtl] .flex-offset-sm-10,[dir=rtl] .offset-sm-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-sm-15,.offset-sm-15{margin-left:15%}[dir=rtl] .flex-offset-sm-15,[dir=rtl] .offset-sm-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-sm-20,.offset-sm-20{margin-left:20%}[dir=rtl] .flex-offset-sm-20,[dir=rtl] .offset-sm-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-sm-25,.offset-sm-25{margin-left:25%}[dir=rtl] .flex-offset-sm-25,[dir=rtl] .offset-sm-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-sm-30,.offset-sm-30{margin-left:30%}[dir=rtl] .flex-offset-sm-30,[dir=rtl] .offset-sm-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-sm-35,.offset-sm-35{margin-left:35%}[dir=rtl] .flex-offset-sm-35,[dir=rtl] .offset-sm-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-sm-40,.offset-sm-40{margin-left:40%}[dir=rtl] .flex-offset-sm-40,[dir=rtl] .offset-sm-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-sm-45,.offset-sm-45{margin-left:45%}[dir=rtl] .flex-offset-sm-45,[dir=rtl] .offset-sm-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-sm-50,.offset-sm-50{margin-left:50%}[dir=rtl] .flex-offset-sm-50,[dir=rtl] .offset-sm-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-sm-55,.offset-sm-55{margin-left:55%}[dir=rtl] .flex-offset-sm-55,[dir=rtl] .offset-sm-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-sm-60,.offset-sm-60{margin-left:60%}[dir=rtl] .flex-offset-sm-60,[dir=rtl] .offset-sm-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-sm-65,.offset-sm-65{margin-left:65%}[dir=rtl] .flex-offset-sm-65,[dir=rtl] .offset-sm-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-sm-70,.offset-sm-70{margin-left:70%}[dir=rtl] .flex-offset-sm-70,[dir=rtl] .offset-sm-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-sm-75,.offset-sm-75{margin-left:75%}[dir=rtl] .flex-offset-sm-75,[dir=rtl] .offset-sm-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-sm-80,.offset-sm-80{margin-left:80%}[dir=rtl] .flex-offset-sm-80,[dir=rtl] .offset-sm-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-sm-85,.offset-sm-85{margin-left:85%}[dir=rtl] .flex-offset-sm-85,[dir=rtl] .offset-sm-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-sm-90,.offset-sm-90{margin-left:90%}[dir=rtl] .flex-offset-sm-90,[dir=rtl] .offset-sm-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-sm-95,.offset-sm-95{margin-left:95%}[dir=rtl] .flex-offset-sm-95,[dir=rtl] .offset-sm-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-sm-33,.offset-sm-33{margin-left:calc(100% / 3)}.flex-offset-sm-66,.offset-sm-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-sm-66,[dir=rtl] .offset-sm-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-sm{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-sm-start,.layout-align-sm-start-center,.layout-align-sm-start-end,.layout-align-sm-start-start,.layout-align-sm-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-sm-center,.layout-align-sm-center-center,.layout-align-sm-center-end,.layout-align-sm-center-start,.layout-align-sm-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-sm-end,.layout-align-sm-end-center,.layout-align-sm-end-end,.layout-align-sm-end-start,.layout-align-sm-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-sm-space-around,.layout-align-sm-space-around-center,.layout-align-sm-space-around-end,.layout-align-sm-space-around-start,.layout-align-sm-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-sm-space-between,.layout-align-sm-space-between-center,.layout-align-sm-space-between-end,.layout-align-sm-space-between-start,.layout-align-sm-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-sm-center-start,.layout-align-sm-end-start,.layout-align-sm-space-around-start,.layout-align-sm-space-between-start,.layout-align-sm-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-sm-center-center,.layout-align-sm-end-center,.layout-align-sm-space-around-center,.layout-align-sm-space-between-center,.layout-align-sm-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-sm-center-center>*,.layout-align-sm-end-center>*,.layout-align-sm-space-around-center>*,.layout-align-sm-space-between-center>*,.layout-align-sm-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-sm-center-end,.layout-align-sm-end-end,.layout-align-sm-space-around-end,.layout-align-sm-space-between-end,.layout-align-sm-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-sm-center-stretch,.layout-align-sm-end-stretch,.layout-align-sm-space-around-stretch,.layout-align-sm-space-between-stretch,.layout-align-sm-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-sm{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-sm-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-sm-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-sm-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-sm-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-sm-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-sm-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-row>.flex-sm-0,.layout-sm-row>.flex-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-sm-0,.layout-sm-column>.flex-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-sm-5,.layout-row>.flex-sm-5,.layout-sm-row>.flex-sm-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-5,.layout-sm-column>.flex-sm-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-sm-10,.layout-row>.flex-sm-10,.layout-sm-row>.flex-sm-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-10,.layout-sm-column>.flex-sm-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-sm-15,.layout-row>.flex-sm-15,.layout-sm-row>.flex-sm-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-15,.layout-sm-column>.flex-sm-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-sm-20,.layout-row>.flex-sm-20,.layout-sm-row>.flex-sm-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-20,.layout-sm-column>.flex-sm-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-sm-25,.layout-row>.flex-sm-25,.layout-sm-row>.flex-sm-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-25,.layout-sm-column>.flex-sm-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-sm-30,.layout-row>.flex-sm-30,.layout-sm-row>.flex-sm-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-30,.layout-sm-column>.flex-sm-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-sm-35,.layout-row>.flex-sm-35,.layout-sm-row>.flex-sm-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-35,.layout-sm-column>.flex-sm-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-sm-40,.layout-row>.flex-sm-40,.layout-sm-row>.flex-sm-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-40,.layout-sm-column>.flex-sm-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-sm-45,.layout-row>.flex-sm-45,.layout-sm-row>.flex-sm-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-45,.layout-sm-column>.flex-sm-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-sm-50,.layout-row>.flex-sm-50,.layout-sm-row>.flex-sm-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-50,.layout-sm-column>.flex-sm-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-sm-55,.layout-row>.flex-sm-55,.layout-sm-row>.flex-sm-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-55,.layout-sm-column>.flex-sm-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-sm-60,.layout-row>.flex-sm-60,.layout-sm-row>.flex-sm-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-60,.layout-sm-column>.flex-sm-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-sm-65,.layout-row>.flex-sm-65,.layout-sm-row>.flex-sm-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-65,.layout-sm-column>.flex-sm-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-sm-70,.layout-row>.flex-sm-70,.layout-sm-row>.flex-sm-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-70,.layout-sm-column>.flex-sm-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-sm-75,.layout-row>.flex-sm-75,.layout-sm-row>.flex-sm-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-75,.layout-sm-column>.flex-sm-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-sm-80,.layout-row>.flex-sm-80,.layout-sm-row>.flex-sm-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-80,.layout-sm-column>.flex-sm-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-sm-85,.layout-row>.flex-sm-85,.layout-sm-row>.flex-sm-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-85,.layout-sm-column>.flex-sm-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-sm-90,.layout-row>.flex-sm-90,.layout-sm-row>.flex-sm-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-90,.layout-sm-column>.flex-sm-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-sm-95,.layout-row>.flex-sm-95,.layout-sm-row>.flex-sm-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-sm-95,.layout-sm-column>.flex-sm-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-sm-100,.layout-column>.flex-sm-100,.layout-row>.flex-sm-100,.layout-sm-column>.flex-sm-100,.layout-sm-row>.flex-sm-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-row>.flex-sm-33,.layout-sm-row>.flex-sm-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-row>.flex-sm-66,.layout-sm-row>.flex-sm-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-row>.flex,.layout-sm-row>.flex{min-width:0}.layout-column>.flex-sm-33,.layout-sm-column>.flex-sm-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-sm-66,.layout-sm-column>.flex-sm-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-sm-column>.flex{min-height:0}.layout-sm,.layout-sm-column,.layout-sm-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-sm-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-sm-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:960px){.flex-order-gt-sm--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-gt-sm--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-gt-sm--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-gt-sm--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-gt-sm--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-gt-sm--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-gt-sm--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-gt-sm--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-gt-sm--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-gt-sm--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-gt-sm--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-gt-sm--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-gt-sm--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-gt-sm--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-gt-sm--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-gt-sm--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-gt-sm--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-gt-sm--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-gt-sm--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-gt-sm--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-gt-sm-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-gt-sm-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-gt-sm-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-gt-sm-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-gt-sm-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-gt-sm-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-gt-sm-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-gt-sm-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-gt-sm-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-gt-sm-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-gt-sm-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-gt-sm-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-gt-sm-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-gt-sm-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-gt-sm-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-gt-sm-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-gt-sm-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-gt-sm-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-gt-sm-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-gt-sm-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-gt-sm-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-gt-sm-0,.offset-gt-sm-0{margin-left:0}[dir=rtl] .flex-offset-gt-sm-0,[dir=rtl] .offset-gt-sm-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-gt-sm-5,.offset-gt-sm-5{margin-left:5%}[dir=rtl] .flex-offset-gt-sm-5,[dir=rtl] .offset-gt-sm-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-gt-sm-10,.offset-gt-sm-10{margin-left:10%}[dir=rtl] .flex-offset-gt-sm-10,[dir=rtl] .offset-gt-sm-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-gt-sm-15,.offset-gt-sm-15{margin-left:15%}[dir=rtl] .flex-offset-gt-sm-15,[dir=rtl] .offset-gt-sm-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-gt-sm-20,.offset-gt-sm-20{margin-left:20%}[dir=rtl] .flex-offset-gt-sm-20,[dir=rtl] .offset-gt-sm-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-gt-sm-25,.offset-gt-sm-25{margin-left:25%}[dir=rtl] .flex-offset-gt-sm-25,[dir=rtl] .offset-gt-sm-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-gt-sm-30,.offset-gt-sm-30{margin-left:30%}[dir=rtl] .flex-offset-gt-sm-30,[dir=rtl] .offset-gt-sm-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-gt-sm-35,.offset-gt-sm-35{margin-left:35%}[dir=rtl] .flex-offset-gt-sm-35,[dir=rtl] .offset-gt-sm-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-gt-sm-40,.offset-gt-sm-40{margin-left:40%}[dir=rtl] .flex-offset-gt-sm-40,[dir=rtl] .offset-gt-sm-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-gt-sm-45,.offset-gt-sm-45{margin-left:45%}[dir=rtl] .flex-offset-gt-sm-45,[dir=rtl] .offset-gt-sm-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-gt-sm-50,.offset-gt-sm-50{margin-left:50%}[dir=rtl] .flex-offset-gt-sm-50,[dir=rtl] .offset-gt-sm-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-gt-sm-55,.offset-gt-sm-55{margin-left:55%}[dir=rtl] .flex-offset-gt-sm-55,[dir=rtl] .offset-gt-sm-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-gt-sm-60,.offset-gt-sm-60{margin-left:60%}[dir=rtl] .flex-offset-gt-sm-60,[dir=rtl] .offset-gt-sm-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-gt-sm-65,.offset-gt-sm-65{margin-left:65%}[dir=rtl] .flex-offset-gt-sm-65,[dir=rtl] .offset-gt-sm-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-gt-sm-70,.offset-gt-sm-70{margin-left:70%}[dir=rtl] .flex-offset-gt-sm-70,[dir=rtl] .offset-gt-sm-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-gt-sm-75,.offset-gt-sm-75{margin-left:75%}[dir=rtl] .flex-offset-gt-sm-75,[dir=rtl] .offset-gt-sm-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-gt-sm-80,.offset-gt-sm-80{margin-left:80%}[dir=rtl] .flex-offset-gt-sm-80,[dir=rtl] .offset-gt-sm-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-gt-sm-85,.offset-gt-sm-85{margin-left:85%}[dir=rtl] .flex-offset-gt-sm-85,[dir=rtl] .offset-gt-sm-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-gt-sm-90,.offset-gt-sm-90{margin-left:90%}[dir=rtl] .flex-offset-gt-sm-90,[dir=rtl] .offset-gt-sm-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-gt-sm-95,.offset-gt-sm-95{margin-left:95%}[dir=rtl] .flex-offset-gt-sm-95,[dir=rtl] .offset-gt-sm-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-gt-sm-33,.offset-gt-sm-33{margin-left:calc(100% / 3)}.flex-offset-gt-sm-66,.offset-gt-sm-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-gt-sm-66,[dir=rtl] .offset-gt-sm-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-gt-sm{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-gt-sm-start,.layout-align-gt-sm-start-center,.layout-align-gt-sm-start-end,.layout-align-gt-sm-start-start,.layout-align-gt-sm-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-gt-sm-center,.layout-align-gt-sm-center-center,.layout-align-gt-sm-center-end,.layout-align-gt-sm-center-start,.layout-align-gt-sm-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-gt-sm-end,.layout-align-gt-sm-end-center,.layout-align-gt-sm-end-end,.layout-align-gt-sm-end-start,.layout-align-gt-sm-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-gt-sm-space-around,.layout-align-gt-sm-space-around-center,.layout-align-gt-sm-space-around-end,.layout-align-gt-sm-space-around-start,.layout-align-gt-sm-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-gt-sm-space-between,.layout-align-gt-sm-space-between-center,.layout-align-gt-sm-space-between-end,.layout-align-gt-sm-space-between-start,.layout-align-gt-sm-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-gt-sm-center-start,.layout-align-gt-sm-end-start,.layout-align-gt-sm-space-around-start,.layout-align-gt-sm-space-between-start,.layout-align-gt-sm-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-gt-sm-center-center,.layout-align-gt-sm-end-center,.layout-align-gt-sm-space-around-center,.layout-align-gt-sm-space-between-center,.layout-align-gt-sm-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-gt-sm-center-center>*,.layout-align-gt-sm-end-center>*,.layout-align-gt-sm-space-around-center>*,.layout-align-gt-sm-space-between-center>*,.layout-align-gt-sm-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-gt-sm-center-end,.layout-align-gt-sm-end-end,.layout-align-gt-sm-space-around-end,.layout-align-gt-sm-space-between-end,.layout-align-gt-sm-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-gt-sm-center-stretch,.layout-align-gt-sm-end-stretch,.layout-align-gt-sm-space-around-stretch,.layout-align-gt-sm-space-between-stretch,.layout-align-gt-sm-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-gt-sm{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-gt-sm-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-gt-sm-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-sm-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-gt-sm-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-gt-sm-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-gt-sm-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-gt-sm-row>.flex-gt-sm-0,.layout-row>.flex-gt-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-gt-sm-0,.layout-gt-sm-column>.flex-gt-sm-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-gt-sm-5,.layout-gt-sm-row>.flex-gt-sm-5,.layout-row>.flex-gt-sm-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-5,.layout-gt-sm-column>.flex-gt-sm-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-gt-sm-10,.layout-gt-sm-row>.flex-gt-sm-10,.layout-row>.flex-gt-sm-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-10,.layout-gt-sm-column>.flex-gt-sm-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-gt-sm-15,.layout-gt-sm-row>.flex-gt-sm-15,.layout-row>.flex-gt-sm-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-15,.layout-gt-sm-column>.flex-gt-sm-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-gt-sm-20,.layout-gt-sm-row>.flex-gt-sm-20,.layout-row>.flex-gt-sm-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-20,.layout-gt-sm-column>.flex-gt-sm-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-gt-sm-25,.layout-gt-sm-row>.flex-gt-sm-25,.layout-row>.flex-gt-sm-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-25,.layout-gt-sm-column>.flex-gt-sm-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-gt-sm-30,.layout-gt-sm-row>.flex-gt-sm-30,.layout-row>.flex-gt-sm-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-30,.layout-gt-sm-column>.flex-gt-sm-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-gt-sm-35,.layout-gt-sm-row>.flex-gt-sm-35,.layout-row>.flex-gt-sm-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-35,.layout-gt-sm-column>.flex-gt-sm-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-gt-sm-40,.layout-gt-sm-row>.flex-gt-sm-40,.layout-row>.flex-gt-sm-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-40,.layout-gt-sm-column>.flex-gt-sm-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-gt-sm-45,.layout-gt-sm-row>.flex-gt-sm-45,.layout-row>.flex-gt-sm-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-45,.layout-gt-sm-column>.flex-gt-sm-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-gt-sm-50,.layout-gt-sm-row>.flex-gt-sm-50,.layout-row>.flex-gt-sm-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-50,.layout-gt-sm-column>.flex-gt-sm-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-gt-sm-55,.layout-gt-sm-row>.flex-gt-sm-55,.layout-row>.flex-gt-sm-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-55,.layout-gt-sm-column>.flex-gt-sm-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-gt-sm-60,.layout-gt-sm-row>.flex-gt-sm-60,.layout-row>.flex-gt-sm-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-60,.layout-gt-sm-column>.flex-gt-sm-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-gt-sm-65,.layout-gt-sm-row>.flex-gt-sm-65,.layout-row>.flex-gt-sm-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-65,.layout-gt-sm-column>.flex-gt-sm-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-gt-sm-70,.layout-gt-sm-row>.flex-gt-sm-70,.layout-row>.flex-gt-sm-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-70,.layout-gt-sm-column>.flex-gt-sm-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-gt-sm-75,.layout-gt-sm-row>.flex-gt-sm-75,.layout-row>.flex-gt-sm-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-75,.layout-gt-sm-column>.flex-gt-sm-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-gt-sm-80,.layout-gt-sm-row>.flex-gt-sm-80,.layout-row>.flex-gt-sm-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-80,.layout-gt-sm-column>.flex-gt-sm-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-gt-sm-85,.layout-gt-sm-row>.flex-gt-sm-85,.layout-row>.flex-gt-sm-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-85,.layout-gt-sm-column>.flex-gt-sm-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-gt-sm-90,.layout-gt-sm-row>.flex-gt-sm-90,.layout-row>.flex-gt-sm-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-90,.layout-gt-sm-column>.flex-gt-sm-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-gt-sm-95,.layout-gt-sm-row>.flex-gt-sm-95,.layout-row>.flex-gt-sm-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-sm-95,.layout-gt-sm-column>.flex-gt-sm-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-gt-sm-100,.layout-column>.flex-gt-sm-100,.layout-gt-sm-column>.flex-gt-sm-100,.layout-gt-sm-row>.flex-gt-sm-100,.layout-row>.flex-gt-sm-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-gt-sm-row>.flex-gt-sm-33,.layout-row>.flex-gt-sm-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-gt-sm-row>.flex-gt-sm-66,.layout-row>.flex-gt-sm-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-gt-sm-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-gt-sm-33,.layout-gt-sm-column>.flex-gt-sm-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-gt-sm-66,.layout-gt-sm-column>.flex-gt-sm-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-gt-sm-column>.flex{min-height:0}.layout-gt-sm,.layout-gt-sm-column,.layout-gt-sm-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-gt-sm-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-gt-sm-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:960px) and (max-width:1279px){.hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show),.hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show),.hide-md:not(.show-md):not(.show-gt-sm):not(.show-gt-xs):not(.show),.hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-md):not(.show){display:none}.flex-order-md--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-md--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-md--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-md--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-md--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-md--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-md--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-md--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-md--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-md--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-md--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-md--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-md--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-md--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-md--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-md--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-md--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-md--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-md--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-md--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-md-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-md-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-md-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-md-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-md-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-md-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-md-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-md-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-md-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-md-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-md-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-md-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-md-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-md-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-md-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-md-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-md-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-md-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-md-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-md-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-md-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-md-0,.offset-md-0{margin-left:0}[dir=rtl] .flex-offset-md-0,[dir=rtl] .offset-md-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-md-5,.offset-md-5{margin-left:5%}[dir=rtl] .flex-offset-md-5,[dir=rtl] .offset-md-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-md-10,.offset-md-10{margin-left:10%}[dir=rtl] .flex-offset-md-10,[dir=rtl] .offset-md-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-md-15,.offset-md-15{margin-left:15%}[dir=rtl] .flex-offset-md-15,[dir=rtl] .offset-md-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-md-20,.offset-md-20{margin-left:20%}[dir=rtl] .flex-offset-md-20,[dir=rtl] .offset-md-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-md-25,.offset-md-25{margin-left:25%}[dir=rtl] .flex-offset-md-25,[dir=rtl] .offset-md-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-md-30,.offset-md-30{margin-left:30%}[dir=rtl] .flex-offset-md-30,[dir=rtl] .offset-md-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-md-35,.offset-md-35{margin-left:35%}[dir=rtl] .flex-offset-md-35,[dir=rtl] .offset-md-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-md-40,.offset-md-40{margin-left:40%}[dir=rtl] .flex-offset-md-40,[dir=rtl] .offset-md-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-md-45,.offset-md-45{margin-left:45%}[dir=rtl] .flex-offset-md-45,[dir=rtl] .offset-md-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-md-50,.offset-md-50{margin-left:50%}[dir=rtl] .flex-offset-md-50,[dir=rtl] .offset-md-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-md-55,.offset-md-55{margin-left:55%}[dir=rtl] .flex-offset-md-55,[dir=rtl] .offset-md-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-md-60,.offset-md-60{margin-left:60%}[dir=rtl] .flex-offset-md-60,[dir=rtl] .offset-md-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-md-65,.offset-md-65{margin-left:65%}[dir=rtl] .flex-offset-md-65,[dir=rtl] .offset-md-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-md-70,.offset-md-70{margin-left:70%}[dir=rtl] .flex-offset-md-70,[dir=rtl] .offset-md-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-md-75,.offset-md-75{margin-left:75%}[dir=rtl] .flex-offset-md-75,[dir=rtl] .offset-md-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-md-80,.offset-md-80{margin-left:80%}[dir=rtl] .flex-offset-md-80,[dir=rtl] .offset-md-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-md-85,.offset-md-85{margin-left:85%}[dir=rtl] .flex-offset-md-85,[dir=rtl] .offset-md-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-md-90,.offset-md-90{margin-left:90%}[dir=rtl] .flex-offset-md-90,[dir=rtl] .offset-md-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-md-95,.offset-md-95{margin-left:95%}[dir=rtl] .flex-offset-md-95,[dir=rtl] .offset-md-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-md-33,.offset-md-33{margin-left:calc(100% / 3)}.flex-offset-md-66,.offset-md-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-md-66,[dir=rtl] .offset-md-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-md{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-md-start,.layout-align-md-start-center,.layout-align-md-start-end,.layout-align-md-start-start,.layout-align-md-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-md-center,.layout-align-md-center-center,.layout-align-md-center-end,.layout-align-md-center-start,.layout-align-md-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-md-end,.layout-align-md-end-center,.layout-align-md-end-end,.layout-align-md-end-start,.layout-align-md-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-md-space-around,.layout-align-md-space-around-center,.layout-align-md-space-around-end,.layout-align-md-space-around-start,.layout-align-md-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-md-space-between,.layout-align-md-space-between-center,.layout-align-md-space-between-end,.layout-align-md-space-between-start,.layout-align-md-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-md-center-start,.layout-align-md-end-start,.layout-align-md-space-around-start,.layout-align-md-space-between-start,.layout-align-md-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-md-center-center,.layout-align-md-end-center,.layout-align-md-space-around-center,.layout-align-md-space-between-center,.layout-align-md-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-md-center-center>*,.layout-align-md-end-center>*,.layout-align-md-space-around-center>*,.layout-align-md-space-between-center>*,.layout-align-md-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-md-center-end,.layout-align-md-end-end,.layout-align-md-space-around-end,.layout-align-md-space-between-end,.layout-align-md-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-md-center-stretch,.layout-align-md-end-stretch,.layout-align-md-space-around-stretch,.layout-align-md-space-between-stretch,.layout-align-md-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-md{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-md-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-md-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-md-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-md-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-md-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-md-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-md-row>.flex-md-0,.layout-row>.flex-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-md-0,.layout-md-column>.flex-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-md-5,.layout-md-row>.flex-md-5,.layout-row>.flex-md-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-5,.layout-md-column>.flex-md-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-md-10,.layout-md-row>.flex-md-10,.layout-row>.flex-md-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-10,.layout-md-column>.flex-md-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-md-15,.layout-md-row>.flex-md-15,.layout-row>.flex-md-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-15,.layout-md-column>.flex-md-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-md-20,.layout-md-row>.flex-md-20,.layout-row>.flex-md-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-20,.layout-md-column>.flex-md-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-md-25,.layout-md-row>.flex-md-25,.layout-row>.flex-md-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-25,.layout-md-column>.flex-md-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-md-30,.layout-md-row>.flex-md-30,.layout-row>.flex-md-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-30,.layout-md-column>.flex-md-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-md-35,.layout-md-row>.flex-md-35,.layout-row>.flex-md-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-35,.layout-md-column>.flex-md-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-md-40,.layout-md-row>.flex-md-40,.layout-row>.flex-md-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-40,.layout-md-column>.flex-md-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-md-45,.layout-md-row>.flex-md-45,.layout-row>.flex-md-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-45,.layout-md-column>.flex-md-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-md-50,.layout-md-row>.flex-md-50,.layout-row>.flex-md-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-50,.layout-md-column>.flex-md-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-md-55,.layout-md-row>.flex-md-55,.layout-row>.flex-md-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-55,.layout-md-column>.flex-md-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-md-60,.layout-md-row>.flex-md-60,.layout-row>.flex-md-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-60,.layout-md-column>.flex-md-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-md-65,.layout-md-row>.flex-md-65,.layout-row>.flex-md-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-65,.layout-md-column>.flex-md-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-md-70,.layout-md-row>.flex-md-70,.layout-row>.flex-md-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-70,.layout-md-column>.flex-md-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-md-75,.layout-md-row>.flex-md-75,.layout-row>.flex-md-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-75,.layout-md-column>.flex-md-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-md-80,.layout-md-row>.flex-md-80,.layout-row>.flex-md-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-80,.layout-md-column>.flex-md-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-md-85,.layout-md-row>.flex-md-85,.layout-row>.flex-md-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-85,.layout-md-column>.flex-md-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-md-90,.layout-md-row>.flex-md-90,.layout-row>.flex-md-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-90,.layout-md-column>.flex-md-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-md-95,.layout-md-row>.flex-md-95,.layout-row>.flex-md-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-md-95,.layout-md-column>.flex-md-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-md-100,.layout-column>.flex-md-100,.layout-md-column>.flex-md-100,.layout-md-row>.flex-md-100,.layout-row>.flex-md-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-md-row>.flex-md-33,.layout-row>.flex-md-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-md-row>.flex-md-66,.layout-row>.flex-md-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-md-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-md-33,.layout-md-column>.flex-md-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-md-66,.layout-md-column>.flex-md-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-md-column>.flex{min-height:0}.layout-md,.layout-md-column,.layout-md-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-md-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-md-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:1280px){.flex-order-gt-md--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-gt-md--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-gt-md--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-gt-md--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-gt-md--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-gt-md--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-gt-md--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-gt-md--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-gt-md--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-gt-md--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-gt-md--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-gt-md--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-gt-md--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-gt-md--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-gt-md--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-gt-md--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-gt-md--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-gt-md--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-gt-md--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-gt-md--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-gt-md-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-gt-md-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-gt-md-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-gt-md-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-gt-md-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-gt-md-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-gt-md-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-gt-md-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-gt-md-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-gt-md-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-gt-md-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-gt-md-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-gt-md-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-gt-md-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-gt-md-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-gt-md-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-gt-md-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-gt-md-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-gt-md-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-gt-md-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-gt-md-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-gt-md-0,.offset-gt-md-0{margin-left:0}[dir=rtl] .flex-offset-gt-md-0,[dir=rtl] .offset-gt-md-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-gt-md-5,.offset-gt-md-5{margin-left:5%}[dir=rtl] .flex-offset-gt-md-5,[dir=rtl] .offset-gt-md-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-gt-md-10,.offset-gt-md-10{margin-left:10%}[dir=rtl] .flex-offset-gt-md-10,[dir=rtl] .offset-gt-md-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-gt-md-15,.offset-gt-md-15{margin-left:15%}[dir=rtl] .flex-offset-gt-md-15,[dir=rtl] .offset-gt-md-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-gt-md-20,.offset-gt-md-20{margin-left:20%}[dir=rtl] .flex-offset-gt-md-20,[dir=rtl] .offset-gt-md-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-gt-md-25,.offset-gt-md-25{margin-left:25%}[dir=rtl] .flex-offset-gt-md-25,[dir=rtl] .offset-gt-md-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-gt-md-30,.offset-gt-md-30{margin-left:30%}[dir=rtl] .flex-offset-gt-md-30,[dir=rtl] .offset-gt-md-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-gt-md-35,.offset-gt-md-35{margin-left:35%}[dir=rtl] .flex-offset-gt-md-35,[dir=rtl] .offset-gt-md-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-gt-md-40,.offset-gt-md-40{margin-left:40%}[dir=rtl] .flex-offset-gt-md-40,[dir=rtl] .offset-gt-md-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-gt-md-45,.offset-gt-md-45{margin-left:45%}[dir=rtl] .flex-offset-gt-md-45,[dir=rtl] .offset-gt-md-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-gt-md-50,.offset-gt-md-50{margin-left:50%}[dir=rtl] .flex-offset-gt-md-50,[dir=rtl] .offset-gt-md-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-gt-md-55,.offset-gt-md-55{margin-left:55%}[dir=rtl] .flex-offset-gt-md-55,[dir=rtl] .offset-gt-md-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-gt-md-60,.offset-gt-md-60{margin-left:60%}[dir=rtl] .flex-offset-gt-md-60,[dir=rtl] .offset-gt-md-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-gt-md-65,.offset-gt-md-65{margin-left:65%}[dir=rtl] .flex-offset-gt-md-65,[dir=rtl] .offset-gt-md-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-gt-md-70,.offset-gt-md-70{margin-left:70%}[dir=rtl] .flex-offset-gt-md-70,[dir=rtl] .offset-gt-md-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-gt-md-75,.offset-gt-md-75{margin-left:75%}[dir=rtl] .flex-offset-gt-md-75,[dir=rtl] .offset-gt-md-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-gt-md-80,.offset-gt-md-80{margin-left:80%}[dir=rtl] .flex-offset-gt-md-80,[dir=rtl] .offset-gt-md-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-gt-md-85,.offset-gt-md-85{margin-left:85%}[dir=rtl] .flex-offset-gt-md-85,[dir=rtl] .offset-gt-md-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-gt-md-90,.offset-gt-md-90{margin-left:90%}[dir=rtl] .flex-offset-gt-md-90,[dir=rtl] .offset-gt-md-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-gt-md-95,.offset-gt-md-95{margin-left:95%}[dir=rtl] .flex-offset-gt-md-95,[dir=rtl] .offset-gt-md-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-gt-md-33,.offset-gt-md-33{margin-left:calc(100% / 3)}.flex-offset-gt-md-66,.offset-gt-md-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-gt-md-66,[dir=rtl] .offset-gt-md-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-gt-md{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-gt-md-start,.layout-align-gt-md-start-center,.layout-align-gt-md-start-end,.layout-align-gt-md-start-start,.layout-align-gt-md-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-gt-md-center,.layout-align-gt-md-center-center,.layout-align-gt-md-center-end,.layout-align-gt-md-center-start,.layout-align-gt-md-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-gt-md-end,.layout-align-gt-md-end-center,.layout-align-gt-md-end-end,.layout-align-gt-md-end-start,.layout-align-gt-md-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-gt-md-space-around,.layout-align-gt-md-space-around-center,.layout-align-gt-md-space-around-end,.layout-align-gt-md-space-around-start,.layout-align-gt-md-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-gt-md-space-between,.layout-align-gt-md-space-between-center,.layout-align-gt-md-space-between-end,.layout-align-gt-md-space-between-start,.layout-align-gt-md-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-gt-md-center-start,.layout-align-gt-md-end-start,.layout-align-gt-md-space-around-start,.layout-align-gt-md-space-between-start,.layout-align-gt-md-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-gt-md-center-center,.layout-align-gt-md-end-center,.layout-align-gt-md-space-around-center,.layout-align-gt-md-space-between-center,.layout-align-gt-md-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-gt-md-center-center>*,.layout-align-gt-md-end-center>*,.layout-align-gt-md-space-around-center>*,.layout-align-gt-md-space-between-center>*,.layout-align-gt-md-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-gt-md-center-end,.layout-align-gt-md-end-end,.layout-align-gt-md-space-around-end,.layout-align-gt-md-space-between-end,.layout-align-gt-md-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-gt-md-center-stretch,.layout-align-gt-md-end-stretch,.layout-align-gt-md-space-around-stretch,.layout-align-gt-md-space-between-stretch,.layout-align-gt-md-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-gt-md{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-gt-md-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-gt-md-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-md-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-gt-md-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-gt-md-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-gt-md-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-gt-md-row>.flex-gt-md-0,.layout-row>.flex-gt-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-gt-md-0,.layout-gt-md-column>.flex-gt-md-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-gt-md-5,.layout-gt-md-row>.flex-gt-md-5,.layout-row>.flex-gt-md-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-5,.layout-gt-md-column>.flex-gt-md-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-gt-md-10,.layout-gt-md-row>.flex-gt-md-10,.layout-row>.flex-gt-md-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-10,.layout-gt-md-column>.flex-gt-md-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-gt-md-15,.layout-gt-md-row>.flex-gt-md-15,.layout-row>.flex-gt-md-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-15,.layout-gt-md-column>.flex-gt-md-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-gt-md-20,.layout-gt-md-row>.flex-gt-md-20,.layout-row>.flex-gt-md-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-20,.layout-gt-md-column>.flex-gt-md-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-gt-md-25,.layout-gt-md-row>.flex-gt-md-25,.layout-row>.flex-gt-md-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-25,.layout-gt-md-column>.flex-gt-md-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-gt-md-30,.layout-gt-md-row>.flex-gt-md-30,.layout-row>.flex-gt-md-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-30,.layout-gt-md-column>.flex-gt-md-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-gt-md-35,.layout-gt-md-row>.flex-gt-md-35,.layout-row>.flex-gt-md-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-35,.layout-gt-md-column>.flex-gt-md-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-gt-md-40,.layout-gt-md-row>.flex-gt-md-40,.layout-row>.flex-gt-md-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-40,.layout-gt-md-column>.flex-gt-md-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-gt-md-45,.layout-gt-md-row>.flex-gt-md-45,.layout-row>.flex-gt-md-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-45,.layout-gt-md-column>.flex-gt-md-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-gt-md-50,.layout-gt-md-row>.flex-gt-md-50,.layout-row>.flex-gt-md-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-50,.layout-gt-md-column>.flex-gt-md-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-gt-md-55,.layout-gt-md-row>.flex-gt-md-55,.layout-row>.flex-gt-md-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-55,.layout-gt-md-column>.flex-gt-md-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-gt-md-60,.layout-gt-md-row>.flex-gt-md-60,.layout-row>.flex-gt-md-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-60,.layout-gt-md-column>.flex-gt-md-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-gt-md-65,.layout-gt-md-row>.flex-gt-md-65,.layout-row>.flex-gt-md-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-65,.layout-gt-md-column>.flex-gt-md-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-gt-md-70,.layout-gt-md-row>.flex-gt-md-70,.layout-row>.flex-gt-md-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-70,.layout-gt-md-column>.flex-gt-md-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-gt-md-75,.layout-gt-md-row>.flex-gt-md-75,.layout-row>.flex-gt-md-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-75,.layout-gt-md-column>.flex-gt-md-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-gt-md-80,.layout-gt-md-row>.flex-gt-md-80,.layout-row>.flex-gt-md-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-80,.layout-gt-md-column>.flex-gt-md-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-gt-md-85,.layout-gt-md-row>.flex-gt-md-85,.layout-row>.flex-gt-md-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-85,.layout-gt-md-column>.flex-gt-md-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-gt-md-90,.layout-gt-md-row>.flex-gt-md-90,.layout-row>.flex-gt-md-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-90,.layout-gt-md-column>.flex-gt-md-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-gt-md-95,.layout-gt-md-row>.flex-gt-md-95,.layout-row>.flex-gt-md-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-md-95,.layout-gt-md-column>.flex-gt-md-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-gt-md-100,.layout-column>.flex-gt-md-100,.layout-gt-md-column>.flex-gt-md-100,.layout-gt-md-row>.flex-gt-md-100,.layout-row>.flex-gt-md-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-gt-md-row>.flex-gt-md-33,.layout-row>.flex-gt-md-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-gt-md-row>.flex-gt-md-66,.layout-row>.flex-gt-md-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-gt-md-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-gt-md-33,.layout-gt-md-column>.flex-gt-md-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-gt-md-66,.layout-gt-md-column>.flex-gt-md-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-gt-md-column>.flex{min-height:0}.layout-gt-md,.layout-gt-md-column,.layout-gt-md-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-gt-md-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-gt-md-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:1280px) and (max-width:1919px){.hide-gt-md:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show),.hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show),.hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show),.hide-lg:not(.show-lg):not(.show-gt-md):not(.show-gt-sm):not(.show-gt-xs):not(.show),.hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-lg):not(.show){display:none}.flex-order-lg--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-lg--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-lg--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-lg--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-lg--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-lg--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-lg--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-lg--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-lg--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-lg--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-lg--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-lg--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-lg--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-lg--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-lg--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-lg--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-lg--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-lg--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-lg--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-lg--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-lg-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-lg-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-lg-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-lg-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-lg-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-lg-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-lg-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-lg-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-lg-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-lg-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-lg-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-lg-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-lg-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-lg-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-lg-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-lg-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-lg-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-lg-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-lg-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-lg-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-lg-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-lg-0,.offset-lg-0{margin-left:0}[dir=rtl] .flex-offset-lg-0,[dir=rtl] .offset-lg-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-lg-5,.offset-lg-5{margin-left:5%}[dir=rtl] .flex-offset-lg-5,[dir=rtl] .offset-lg-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-lg-10,.offset-lg-10{margin-left:10%}[dir=rtl] .flex-offset-lg-10,[dir=rtl] .offset-lg-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-lg-15,.offset-lg-15{margin-left:15%}[dir=rtl] .flex-offset-lg-15,[dir=rtl] .offset-lg-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-lg-20,.offset-lg-20{margin-left:20%}[dir=rtl] .flex-offset-lg-20,[dir=rtl] .offset-lg-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-lg-25,.offset-lg-25{margin-left:25%}[dir=rtl] .flex-offset-lg-25,[dir=rtl] .offset-lg-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-lg-30,.offset-lg-30{margin-left:30%}[dir=rtl] .flex-offset-lg-30,[dir=rtl] .offset-lg-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-lg-35,.offset-lg-35{margin-left:35%}[dir=rtl] .flex-offset-lg-35,[dir=rtl] .offset-lg-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-lg-40,.offset-lg-40{margin-left:40%}[dir=rtl] .flex-offset-lg-40,[dir=rtl] .offset-lg-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-lg-45,.offset-lg-45{margin-left:45%}[dir=rtl] .flex-offset-lg-45,[dir=rtl] .offset-lg-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-lg-50,.offset-lg-50{margin-left:50%}[dir=rtl] .flex-offset-lg-50,[dir=rtl] .offset-lg-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-lg-55,.offset-lg-55{margin-left:55%}[dir=rtl] .flex-offset-lg-55,[dir=rtl] .offset-lg-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-lg-60,.offset-lg-60{margin-left:60%}[dir=rtl] .flex-offset-lg-60,[dir=rtl] .offset-lg-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-lg-65,.offset-lg-65{margin-left:65%}[dir=rtl] .flex-offset-lg-65,[dir=rtl] .offset-lg-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-lg-70,.offset-lg-70{margin-left:70%}[dir=rtl] .flex-offset-lg-70,[dir=rtl] .offset-lg-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-lg-75,.offset-lg-75{margin-left:75%}[dir=rtl] .flex-offset-lg-75,[dir=rtl] .offset-lg-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-lg-80,.offset-lg-80{margin-left:80%}[dir=rtl] .flex-offset-lg-80,[dir=rtl] .offset-lg-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-lg-85,.offset-lg-85{margin-left:85%}[dir=rtl] .flex-offset-lg-85,[dir=rtl] .offset-lg-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-lg-90,.offset-lg-90{margin-left:90%}[dir=rtl] .flex-offset-lg-90,[dir=rtl] .offset-lg-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-lg-95,.offset-lg-95{margin-left:95%}[dir=rtl] .flex-offset-lg-95,[dir=rtl] .offset-lg-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-lg-33,.offset-lg-33{margin-left:calc(100% / 3)}.flex-offset-lg-66,.offset-lg-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-lg-66,[dir=rtl] .offset-lg-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-lg{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-lg-start,.layout-align-lg-start-center,.layout-align-lg-start-end,.layout-align-lg-start-start,.layout-align-lg-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-lg-center,.layout-align-lg-center-center,.layout-align-lg-center-end,.layout-align-lg-center-start,.layout-align-lg-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-lg-end,.layout-align-lg-end-center,.layout-align-lg-end-end,.layout-align-lg-end-start,.layout-align-lg-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-lg-space-around,.layout-align-lg-space-around-center,.layout-align-lg-space-around-end,.layout-align-lg-space-around-start,.layout-align-lg-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-lg-space-between,.layout-align-lg-space-between-center,.layout-align-lg-space-between-end,.layout-align-lg-space-between-start,.layout-align-lg-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-lg-center-start,.layout-align-lg-end-start,.layout-align-lg-space-around-start,.layout-align-lg-space-between-start,.layout-align-lg-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-lg-center-center,.layout-align-lg-end-center,.layout-align-lg-space-around-center,.layout-align-lg-space-between-center,.layout-align-lg-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-lg-center-center>*,.layout-align-lg-end-center>*,.layout-align-lg-space-around-center>*,.layout-align-lg-space-between-center>*,.layout-align-lg-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-lg-center-end,.layout-align-lg-end-end,.layout-align-lg-space-around-end,.layout-align-lg-space-between-end,.layout-align-lg-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-lg-center-stretch,.layout-align-lg-end-stretch,.layout-align-lg-space-around-stretch,.layout-align-lg-space-between-stretch,.layout-align-lg-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-lg{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-lg-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-lg-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-lg-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-lg-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-lg-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-lg-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-lg-row>.flex-lg-0,.layout-row>.flex-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-lg-0,.layout-lg-column>.flex-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-lg-5,.layout-lg-row>.flex-lg-5,.layout-row>.flex-lg-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-5,.layout-lg-column>.flex-lg-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-lg-10,.layout-lg-row>.flex-lg-10,.layout-row>.flex-lg-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-10,.layout-lg-column>.flex-lg-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-lg-15,.layout-lg-row>.flex-lg-15,.layout-row>.flex-lg-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-15,.layout-lg-column>.flex-lg-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-lg-20,.layout-lg-row>.flex-lg-20,.layout-row>.flex-lg-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-20,.layout-lg-column>.flex-lg-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-lg-25,.layout-lg-row>.flex-lg-25,.layout-row>.flex-lg-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-25,.layout-lg-column>.flex-lg-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-lg-30,.layout-lg-row>.flex-lg-30,.layout-row>.flex-lg-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-30,.layout-lg-column>.flex-lg-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-lg-35,.layout-lg-row>.flex-lg-35,.layout-row>.flex-lg-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-35,.layout-lg-column>.flex-lg-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-lg-40,.layout-lg-row>.flex-lg-40,.layout-row>.flex-lg-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-40,.layout-lg-column>.flex-lg-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-lg-45,.layout-lg-row>.flex-lg-45,.layout-row>.flex-lg-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-45,.layout-lg-column>.flex-lg-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-lg-50,.layout-lg-row>.flex-lg-50,.layout-row>.flex-lg-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-50,.layout-lg-column>.flex-lg-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-lg-55,.layout-lg-row>.flex-lg-55,.layout-row>.flex-lg-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-55,.layout-lg-column>.flex-lg-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-lg-60,.layout-lg-row>.flex-lg-60,.layout-row>.flex-lg-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-60,.layout-lg-column>.flex-lg-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-lg-65,.layout-lg-row>.flex-lg-65,.layout-row>.flex-lg-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-65,.layout-lg-column>.flex-lg-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-lg-70,.layout-lg-row>.flex-lg-70,.layout-row>.flex-lg-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-70,.layout-lg-column>.flex-lg-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-lg-75,.layout-lg-row>.flex-lg-75,.layout-row>.flex-lg-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-75,.layout-lg-column>.flex-lg-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-lg-80,.layout-lg-row>.flex-lg-80,.layout-row>.flex-lg-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-80,.layout-lg-column>.flex-lg-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-lg-85,.layout-lg-row>.flex-lg-85,.layout-row>.flex-lg-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-85,.layout-lg-column>.flex-lg-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-lg-90,.layout-lg-row>.flex-lg-90,.layout-row>.flex-lg-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-90,.layout-lg-column>.flex-lg-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-lg-95,.layout-lg-row>.flex-lg-95,.layout-row>.flex-lg-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-lg-95,.layout-lg-column>.flex-lg-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-lg-100,.layout-column>.flex-lg-100,.layout-lg-column>.flex-lg-100,.layout-lg-row>.flex-lg-100,.layout-row>.flex-lg-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-lg-row>.flex-lg-33,.layout-row>.flex-lg-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-lg-row>.flex-lg-66,.layout-row>.flex-lg-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-lg-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-lg-33,.layout-lg-column>.flex-lg-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-lg-66,.layout-lg-column>.flex-lg-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-lg-column>.flex{min-height:0}.layout-lg,.layout-lg-column,.layout-lg-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-lg-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-lg-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}}@media (min-width:1920px){.flex-order-gt-lg--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-gt-lg--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-gt-lg--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-gt-lg--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-gt-lg--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-gt-lg--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-gt-lg--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-gt-lg--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-gt-lg--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-gt-lg--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-gt-lg--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-gt-lg--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-gt-lg--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-gt-lg--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-gt-lg--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-gt-lg--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-gt-lg--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-gt-lg--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-gt-lg--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-gt-lg--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-gt-lg-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-gt-lg-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-gt-lg-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-gt-lg-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-gt-lg-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-gt-lg-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-gt-lg-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-gt-lg-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-gt-lg-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-gt-lg-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-gt-lg-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-gt-lg-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-gt-lg-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-gt-lg-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-gt-lg-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-gt-lg-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-gt-lg-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-gt-lg-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-gt-lg-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-gt-lg-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-gt-lg-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-gt-lg-0,.offset-gt-lg-0{margin-left:0}[dir=rtl] .flex-offset-gt-lg-0,[dir=rtl] .offset-gt-lg-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-gt-lg-5,.offset-gt-lg-5{margin-left:5%}[dir=rtl] .flex-offset-gt-lg-5,[dir=rtl] .offset-gt-lg-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-gt-lg-10,.offset-gt-lg-10{margin-left:10%}[dir=rtl] .flex-offset-gt-lg-10,[dir=rtl] .offset-gt-lg-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-gt-lg-15,.offset-gt-lg-15{margin-left:15%}[dir=rtl] .flex-offset-gt-lg-15,[dir=rtl] .offset-gt-lg-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-gt-lg-20,.offset-gt-lg-20{margin-left:20%}[dir=rtl] .flex-offset-gt-lg-20,[dir=rtl] .offset-gt-lg-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-gt-lg-25,.offset-gt-lg-25{margin-left:25%}[dir=rtl] .flex-offset-gt-lg-25,[dir=rtl] .offset-gt-lg-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-gt-lg-30,.offset-gt-lg-30{margin-left:30%}[dir=rtl] .flex-offset-gt-lg-30,[dir=rtl] .offset-gt-lg-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-gt-lg-35,.offset-gt-lg-35{margin-left:35%}[dir=rtl] .flex-offset-gt-lg-35,[dir=rtl] .offset-gt-lg-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-gt-lg-40,.offset-gt-lg-40{margin-left:40%}[dir=rtl] .flex-offset-gt-lg-40,[dir=rtl] .offset-gt-lg-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-gt-lg-45,.offset-gt-lg-45{margin-left:45%}[dir=rtl] .flex-offset-gt-lg-45,[dir=rtl] .offset-gt-lg-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-gt-lg-50,.offset-gt-lg-50{margin-left:50%}[dir=rtl] .flex-offset-gt-lg-50,[dir=rtl] .offset-gt-lg-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-gt-lg-55,.offset-gt-lg-55{margin-left:55%}[dir=rtl] .flex-offset-gt-lg-55,[dir=rtl] .offset-gt-lg-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-gt-lg-60,.offset-gt-lg-60{margin-left:60%}[dir=rtl] .flex-offset-gt-lg-60,[dir=rtl] .offset-gt-lg-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-gt-lg-65,.offset-gt-lg-65{margin-left:65%}[dir=rtl] .flex-offset-gt-lg-65,[dir=rtl] .offset-gt-lg-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-gt-lg-70,.offset-gt-lg-70{margin-left:70%}[dir=rtl] .flex-offset-gt-lg-70,[dir=rtl] .offset-gt-lg-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-gt-lg-75,.offset-gt-lg-75{margin-left:75%}[dir=rtl] .flex-offset-gt-lg-75,[dir=rtl] .offset-gt-lg-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-gt-lg-80,.offset-gt-lg-80{margin-left:80%}[dir=rtl] .flex-offset-gt-lg-80,[dir=rtl] .offset-gt-lg-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-gt-lg-85,.offset-gt-lg-85{margin-left:85%}[dir=rtl] .flex-offset-gt-lg-85,[dir=rtl] .offset-gt-lg-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-gt-lg-90,.offset-gt-lg-90{margin-left:90%}[dir=rtl] .flex-offset-gt-lg-90,[dir=rtl] .offset-gt-lg-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-gt-lg-95,.offset-gt-lg-95{margin-left:95%}[dir=rtl] .flex-offset-gt-lg-95,[dir=rtl] .offset-gt-lg-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-gt-lg-33,.offset-gt-lg-33{margin-left:calc(100% / 3)}.flex-offset-gt-lg-66,.offset-gt-lg-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-gt-lg-66,[dir=rtl] .offset-gt-lg-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-gt-lg{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-gt-lg-start,.layout-align-gt-lg-start-center,.layout-align-gt-lg-start-end,.layout-align-gt-lg-start-start,.layout-align-gt-lg-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-gt-lg-center,.layout-align-gt-lg-center-center,.layout-align-gt-lg-center-end,.layout-align-gt-lg-center-start,.layout-align-gt-lg-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-gt-lg-end,.layout-align-gt-lg-end-center,.layout-align-gt-lg-end-end,.layout-align-gt-lg-end-start,.layout-align-gt-lg-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-gt-lg-space-around,.layout-align-gt-lg-space-around-center,.layout-align-gt-lg-space-around-end,.layout-align-gt-lg-space-around-start,.layout-align-gt-lg-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-gt-lg-space-between,.layout-align-gt-lg-space-between-center,.layout-align-gt-lg-space-between-end,.layout-align-gt-lg-space-between-start,.layout-align-gt-lg-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-gt-lg-center-start,.layout-align-gt-lg-end-start,.layout-align-gt-lg-space-around-start,.layout-align-gt-lg-space-between-start,.layout-align-gt-lg-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-gt-lg-center-center,.layout-align-gt-lg-end-center,.layout-align-gt-lg-space-around-center,.layout-align-gt-lg-space-between-center,.layout-align-gt-lg-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-gt-lg-center-center>*,.layout-align-gt-lg-end-center>*,.layout-align-gt-lg-space-around-center>*,.layout-align-gt-lg-space-between-center>*,.layout-align-gt-lg-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-gt-lg-center-end,.layout-align-gt-lg-end-end,.layout-align-gt-lg-space-around-end,.layout-align-gt-lg-space-between-end,.layout-align-gt-lg-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-gt-lg-center-stretch,.layout-align-gt-lg-end-stretch,.layout-align-gt-lg-space-around-stretch,.layout-align-gt-lg-space-between-stretch,.layout-align-gt-lg-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-gt-lg{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-gt-lg-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-gt-lg-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-lg-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-gt-lg-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-gt-lg-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-gt-lg-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-gt-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-gt-lg-row>.flex-gt-lg-0,.layout-row>.flex-gt-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-gt-lg-0,.layout-gt-lg-column>.flex-gt-lg-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-gt-lg-5,.layout-gt-lg-row>.flex-gt-lg-5,.layout-row>.flex-gt-lg-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-5,.layout-gt-lg-column>.flex-gt-lg-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-gt-lg-10,.layout-gt-lg-row>.flex-gt-lg-10,.layout-row>.flex-gt-lg-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-10,.layout-gt-lg-column>.flex-gt-lg-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-gt-lg-15,.layout-gt-lg-row>.flex-gt-lg-15,.layout-row>.flex-gt-lg-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-15,.layout-gt-lg-column>.flex-gt-lg-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-gt-lg-20,.layout-gt-lg-row>.flex-gt-lg-20,.layout-row>.flex-gt-lg-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-20,.layout-gt-lg-column>.flex-gt-lg-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-gt-lg-25,.layout-gt-lg-row>.flex-gt-lg-25,.layout-row>.flex-gt-lg-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-25,.layout-gt-lg-column>.flex-gt-lg-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-gt-lg-30,.layout-gt-lg-row>.flex-gt-lg-30,.layout-row>.flex-gt-lg-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-30,.layout-gt-lg-column>.flex-gt-lg-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-gt-lg-35,.layout-gt-lg-row>.flex-gt-lg-35,.layout-row>.flex-gt-lg-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-35,.layout-gt-lg-column>.flex-gt-lg-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-gt-lg-40,.layout-gt-lg-row>.flex-gt-lg-40,.layout-row>.flex-gt-lg-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-40,.layout-gt-lg-column>.flex-gt-lg-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-gt-lg-45,.layout-gt-lg-row>.flex-gt-lg-45,.layout-row>.flex-gt-lg-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-45,.layout-gt-lg-column>.flex-gt-lg-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-gt-lg-50,.layout-gt-lg-row>.flex-gt-lg-50,.layout-row>.flex-gt-lg-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-50,.layout-gt-lg-column>.flex-gt-lg-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-gt-lg-55,.layout-gt-lg-row>.flex-gt-lg-55,.layout-row>.flex-gt-lg-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-55,.layout-gt-lg-column>.flex-gt-lg-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-gt-lg-60,.layout-gt-lg-row>.flex-gt-lg-60,.layout-row>.flex-gt-lg-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-60,.layout-gt-lg-column>.flex-gt-lg-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-gt-lg-65,.layout-gt-lg-row>.flex-gt-lg-65,.layout-row>.flex-gt-lg-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-65,.layout-gt-lg-column>.flex-gt-lg-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-gt-lg-70,.layout-gt-lg-row>.flex-gt-lg-70,.layout-row>.flex-gt-lg-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-70,.layout-gt-lg-column>.flex-gt-lg-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-gt-lg-75,.layout-gt-lg-row>.flex-gt-lg-75,.layout-row>.flex-gt-lg-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-75,.layout-gt-lg-column>.flex-gt-lg-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-gt-lg-80,.layout-gt-lg-row>.flex-gt-lg-80,.layout-row>.flex-gt-lg-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-80,.layout-gt-lg-column>.flex-gt-lg-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-gt-lg-85,.layout-gt-lg-row>.flex-gt-lg-85,.layout-row>.flex-gt-lg-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-85,.layout-gt-lg-column>.flex-gt-lg-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-gt-lg-90,.layout-gt-lg-row>.flex-gt-lg-90,.layout-row>.flex-gt-lg-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-90,.layout-gt-lg-column>.flex-gt-lg-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-gt-lg-95,.layout-gt-lg-row>.flex-gt-lg-95,.layout-row>.flex-gt-lg-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-gt-lg-95,.layout-gt-lg-column>.flex-gt-lg-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-gt-lg-100,.layout-column>.flex-gt-lg-100,.layout-gt-lg-column>.flex-gt-lg-100,.layout-gt-lg-row>.flex-gt-lg-100,.layout-row>.flex-gt-lg-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-gt-lg-row>.flex-gt-lg-33,.layout-row>.flex-gt-lg-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-gt-lg-row>.flex-gt-lg-66,.layout-row>.flex-gt-lg-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-gt-lg-row>.flex,.layout-row>.flex{min-width:0}.layout-column>.flex-gt-lg-33,.layout-gt-lg-column>.flex-gt-lg-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-gt-lg-66,.layout-gt-lg-column>.flex-gt-lg-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-gt-lg-column>.flex{min-height:0}.layout-gt-lg,.layout-gt-lg-column,.layout-gt-lg-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-gt-lg-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-gt-lg-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.flex-order-xl--20{-webkit-order:-20;-ms-flex-order:-20;order:-20}.flex-order-xl--19{-webkit-order:-19;-ms-flex-order:-19;order:-19}.flex-order-xl--18{-webkit-order:-18;-ms-flex-order:-18;order:-18}.flex-order-xl--17{-webkit-order:-17;-ms-flex-order:-17;order:-17}.flex-order-xl--16{-webkit-order:-16;-ms-flex-order:-16;order:-16}.flex-order-xl--15{-webkit-order:-15;-ms-flex-order:-15;order:-15}.flex-order-xl--14{-webkit-order:-14;-ms-flex-order:-14;order:-14}.flex-order-xl--13{-webkit-order:-13;-ms-flex-order:-13;order:-13}.flex-order-xl--12{-webkit-order:-12;-ms-flex-order:-12;order:-12}.flex-order-xl--11{-webkit-order:-11;-ms-flex-order:-11;order:-11}.flex-order-xl--10{-webkit-order:-10;-ms-flex-order:-10;order:-10}.flex-order-xl--9{-webkit-order:-9;-ms-flex-order:-9;order:-9}.flex-order-xl--8{-webkit-order:-8;-ms-flex-order:-8;order:-8}.flex-order-xl--7{-webkit-order:-7;-ms-flex-order:-7;order:-7}.flex-order-xl--6{-webkit-order:-6;-ms-flex-order:-6;order:-6}.flex-order-xl--5{-webkit-order:-5;-ms-flex-order:-5;order:-5}.flex-order-xl--4{-webkit-order:-4;-ms-flex-order:-4;order:-4}.flex-order-xl--3{-webkit-order:-3;-ms-flex-order:-3;order:-3}.flex-order-xl--2{-webkit-order:-2;-ms-flex-order:-2;order:-2}.flex-order-xl--1{-webkit-order:-1;-ms-flex-order:-1;order:-1}.flex-order-xl-0{-webkit-order:0;-ms-flex-order:0;order:0}.flex-order-xl-1{-webkit-order:1;-ms-flex-order:1;order:1}.flex-order-xl-2{-webkit-order:2;-ms-flex-order:2;order:2}.flex-order-xl-3{-webkit-order:3;-ms-flex-order:3;order:3}.flex-order-xl-4{-webkit-order:4;-ms-flex-order:4;order:4}.flex-order-xl-5{-webkit-order:5;-ms-flex-order:5;order:5}.flex-order-xl-6{-webkit-order:6;-ms-flex-order:6;order:6}.flex-order-xl-7{-webkit-order:7;-ms-flex-order:7;order:7}.flex-order-xl-8{-webkit-order:8;-ms-flex-order:8;order:8}.flex-order-xl-9{-webkit-order:9;-ms-flex-order:9;order:9}.flex-order-xl-10{-webkit-order:10;-ms-flex-order:10;order:10}.flex-order-xl-11{-webkit-order:11;-ms-flex-order:11;order:11}.flex-order-xl-12{-webkit-order:12;-ms-flex-order:12;order:12}.flex-order-xl-13{-webkit-order:13;-ms-flex-order:13;order:13}.flex-order-xl-14{-webkit-order:14;-ms-flex-order:14;order:14}.flex-order-xl-15{-webkit-order:15;-ms-flex-order:15;order:15}.flex-order-xl-16{-webkit-order:16;-ms-flex-order:16;order:16}.flex-order-xl-17{-webkit-order:17;-ms-flex-order:17;order:17}.flex-order-xl-18{-webkit-order:18;-ms-flex-order:18;order:18}.flex-order-xl-19{-webkit-order:19;-ms-flex-order:19;order:19}.flex-order-xl-20{-webkit-order:20;-ms-flex-order:20;order:20}.flex-offset-xl-0,.offset-xl-0{margin-left:0}[dir=rtl] .flex-offset-xl-0,[dir=rtl] .offset-xl-0{margin-left:auto;margin-left:initial;margin-right:0}.flex-offset-xl-5,.offset-xl-5{margin-left:5%}[dir=rtl] .flex-offset-xl-5,[dir=rtl] .offset-xl-5{margin-left:auto;margin-left:initial;margin-right:5%}.flex-offset-xl-10,.offset-xl-10{margin-left:10%}[dir=rtl] .flex-offset-xl-10,[dir=rtl] .offset-xl-10{margin-left:auto;margin-left:initial;margin-right:10%}.flex-offset-xl-15,.offset-xl-15{margin-left:15%}[dir=rtl] .flex-offset-xl-15,[dir=rtl] .offset-xl-15{margin-left:auto;margin-left:initial;margin-right:15%}.flex-offset-xl-20,.offset-xl-20{margin-left:20%}[dir=rtl] .flex-offset-xl-20,[dir=rtl] .offset-xl-20{margin-left:auto;margin-left:initial;margin-right:20%}.flex-offset-xl-25,.offset-xl-25{margin-left:25%}[dir=rtl] .flex-offset-xl-25,[dir=rtl] .offset-xl-25{margin-left:auto;margin-left:initial;margin-right:25%}.flex-offset-xl-30,.offset-xl-30{margin-left:30%}[dir=rtl] .flex-offset-xl-30,[dir=rtl] .offset-xl-30{margin-left:auto;margin-left:initial;margin-right:30%}.flex-offset-xl-35,.offset-xl-35{margin-left:35%}[dir=rtl] .flex-offset-xl-35,[dir=rtl] .offset-xl-35{margin-left:auto;margin-left:initial;margin-right:35%}.flex-offset-xl-40,.offset-xl-40{margin-left:40%}[dir=rtl] .flex-offset-xl-40,[dir=rtl] .offset-xl-40{margin-left:auto;margin-left:initial;margin-right:40%}.flex-offset-xl-45,.offset-xl-45{margin-left:45%}[dir=rtl] .flex-offset-xl-45,[dir=rtl] .offset-xl-45{margin-left:auto;margin-left:initial;margin-right:45%}.flex-offset-xl-50,.offset-xl-50{margin-left:50%}[dir=rtl] .flex-offset-xl-50,[dir=rtl] .offset-xl-50{margin-left:auto;margin-left:initial;margin-right:50%}.flex-offset-xl-55,.offset-xl-55{margin-left:55%}[dir=rtl] .flex-offset-xl-55,[dir=rtl] .offset-xl-55{margin-left:auto;margin-left:initial;margin-right:55%}.flex-offset-xl-60,.offset-xl-60{margin-left:60%}[dir=rtl] .flex-offset-xl-60,[dir=rtl] .offset-xl-60{margin-left:auto;margin-left:initial;margin-right:60%}.flex-offset-xl-65,.offset-xl-65{margin-left:65%}[dir=rtl] .flex-offset-xl-65,[dir=rtl] .offset-xl-65{margin-left:auto;margin-left:initial;margin-right:65%}.flex-offset-xl-70,.offset-xl-70{margin-left:70%}[dir=rtl] .flex-offset-xl-70,[dir=rtl] .offset-xl-70{margin-left:auto;margin-left:initial;margin-right:70%}.flex-offset-xl-75,.offset-xl-75{margin-left:75%}[dir=rtl] .flex-offset-xl-75,[dir=rtl] .offset-xl-75{margin-left:auto;margin-left:initial;margin-right:75%}.flex-offset-xl-80,.offset-xl-80{margin-left:80%}[dir=rtl] .flex-offset-xl-80,[dir=rtl] .offset-xl-80{margin-left:auto;margin-left:initial;margin-right:80%}.flex-offset-xl-85,.offset-xl-85{margin-left:85%}[dir=rtl] .flex-offset-xl-85,[dir=rtl] .offset-xl-85{margin-left:auto;margin-left:initial;margin-right:85%}.flex-offset-xl-90,.offset-xl-90{margin-left:90%}[dir=rtl] .flex-offset-xl-90,[dir=rtl] .offset-xl-90{margin-left:auto;margin-left:initial;margin-right:90%}.flex-offset-xl-95,.offset-xl-95{margin-left:95%}[dir=rtl] .flex-offset-xl-95,[dir=rtl] .offset-xl-95{margin-left:auto;margin-left:initial;margin-right:95%}.flex-offset-xl-33,.offset-xl-33{margin-left:calc(100% / 3)}.flex-offset-xl-66,.offset-xl-66{margin-left:calc(200% / 3)}[dir=rtl] .flex-offset-xl-66,[dir=rtl] .offset-xl-66{margin-left:auto;margin-left:initial;margin-right:calc(200% / 3)}.layout-align-xl{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.layout-align-xl-start,.layout-align-xl-start-center,.layout-align-xl-start-end,.layout-align-xl-start-start,.layout-align-xl-start-stretch{-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.layout-align-xl-center,.layout-align-xl-center-center,.layout-align-xl-center-end,.layout-align-xl-center-start,.layout-align-xl-center-stretch{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.layout-align-xl-end,.layout-align-xl-end-center,.layout-align-xl-end-end,.layout-align-xl-end-start,.layout-align-xl-end-stretch{-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.layout-align-xl-space-around,.layout-align-xl-space-around-center,.layout-align-xl-space-around-end,.layout-align-xl-space-around-start,.layout-align-xl-space-around-stretch{-webkit-justify-content:space-around;-ms-flex-pack:distribute;justify-content:space-around}.layout-align-xl-space-between,.layout-align-xl-space-between-center,.layout-align-xl-space-between-end,.layout-align-xl-space-between-start,.layout-align-xl-space-between-stretch{-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.layout-align-xl-center-start,.layout-align-xl-end-start,.layout-align-xl-space-around-start,.layout-align-xl-space-between-start,.layout-align-xl-start-start{-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.layout-align-xl-center-center,.layout-align-xl-end-center,.layout-align-xl-space-around-center,.layout-align-xl-space-between-center,.layout-align-xl-start-center{-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;max-width:100%}.layout-align-xl-center-center>*,.layout-align-xl-end-center>*,.layout-align-xl-space-around-center>*,.layout-align-xl-space-between-center>*,.layout-align-xl-start-center>*{max-width:100%;box-sizing:border-box}.layout-align-xl-center-end,.layout-align-xl-end-end,.layout-align-xl-space-around-end,.layout-align-xl-space-between-end,.layout-align-xl-start-end{-webkit-align-items:flex-end;-ms-flex-align:end;align-items:flex-end;-webkit-align-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.layout-align-xl-center-stretch,.layout-align-xl-end-stretch,.layout-align-xl-space-around-stretch,.layout-align-xl-space-between-stretch,.layout-align-xl-start-stretch{-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-align-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch}.flex-xl{-webkit-flex:1;-ms-flex:1;flex:1;box-sizing:border-box}.flex-xl-grow{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;box-sizing:border-box}.flex-xl-initial{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-xl-auto{-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;box-sizing:border-box}.flex-xl-none{-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;box-sizing:border-box}.flex-xl-noshrink{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;box-sizing:border-box}.flex-xl-nogrow{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto;box-sizing:border-box}.flex-xl-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box}.layout-row>.flex-xl-0,.layout-xl-row>.flex-xl-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:0;max-height:100%;box-sizing:border-box;min-width:0}.layout-column>.flex-xl-0,.layout-xl-column>.flex-xl-0{-webkit-flex:1 1 0;-ms-flex:1 1 0;flex:1 1 0;max-width:100%;max-height:0;box-sizing:border-box;min-height:0}.flex-xl-5,.layout-row>.flex-xl-5,.layout-xl-row>.flex-xl-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:5%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-5,.layout-xl-column>.flex-xl-5{-webkit-flex:1 1 5%;-ms-flex:1 1 5%;flex:1 1 5%;max-width:100%;max-height:5%;box-sizing:border-box}.flex-xl-10,.layout-row>.flex-xl-10,.layout-xl-row>.flex-xl-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:10%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-10,.layout-xl-column>.flex-xl-10{-webkit-flex:1 1 10%;-ms-flex:1 1 10%;flex:1 1 10%;max-width:100%;max-height:10%;box-sizing:border-box}.flex-xl-15,.layout-row>.flex-xl-15,.layout-xl-row>.flex-xl-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:15%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-15,.layout-xl-column>.flex-xl-15{-webkit-flex:1 1 15%;-ms-flex:1 1 15%;flex:1 1 15%;max-width:100%;max-height:15%;box-sizing:border-box}.flex-xl-20,.layout-row>.flex-xl-20,.layout-xl-row>.flex-xl-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:20%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-20,.layout-xl-column>.flex-xl-20{-webkit-flex:1 1 20%;-ms-flex:1 1 20%;flex:1 1 20%;max-width:100%;max-height:20%;box-sizing:border-box}.flex-xl-25,.layout-row>.flex-xl-25,.layout-xl-row>.flex-xl-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:25%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-25,.layout-xl-column>.flex-xl-25{-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%;max-width:100%;max-height:25%;box-sizing:border-box}.flex-xl-30,.layout-row>.flex-xl-30,.layout-xl-row>.flex-xl-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:30%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-30,.layout-xl-column>.flex-xl-30{-webkit-flex:1 1 30%;-ms-flex:1 1 30%;flex:1 1 30%;max-width:100%;max-height:30%;box-sizing:border-box}.flex-xl-35,.layout-row>.flex-xl-35,.layout-xl-row>.flex-xl-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:35%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-35,.layout-xl-column>.flex-xl-35{-webkit-flex:1 1 35%;-ms-flex:1 1 35%;flex:1 1 35%;max-width:100%;max-height:35%;box-sizing:border-box}.flex-xl-40,.layout-row>.flex-xl-40,.layout-xl-row>.flex-xl-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:40%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-40,.layout-xl-column>.flex-xl-40{-webkit-flex:1 1 40%;-ms-flex:1 1 40%;flex:1 1 40%;max-width:100%;max-height:40%;box-sizing:border-box}.flex-xl-45,.layout-row>.flex-xl-45,.layout-xl-row>.flex-xl-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:45%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-45,.layout-xl-column>.flex-xl-45{-webkit-flex:1 1 45%;-ms-flex:1 1 45%;flex:1 1 45%;max-width:100%;max-height:45%;box-sizing:border-box}.flex-xl-50,.layout-row>.flex-xl-50,.layout-xl-row>.flex-xl-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:50%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-50,.layout-xl-column>.flex-xl-50{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;max-width:100%;max-height:50%;box-sizing:border-box}.flex-xl-55,.layout-row>.flex-xl-55,.layout-xl-row>.flex-xl-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:55%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-55,.layout-xl-column>.flex-xl-55{-webkit-flex:1 1 55%;-ms-flex:1 1 55%;flex:1 1 55%;max-width:100%;max-height:55%;box-sizing:border-box}.flex-xl-60,.layout-row>.flex-xl-60,.layout-xl-row>.flex-xl-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:60%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-60,.layout-xl-column>.flex-xl-60{-webkit-flex:1 1 60%;-ms-flex:1 1 60%;flex:1 1 60%;max-width:100%;max-height:60%;box-sizing:border-box}.flex-xl-65,.layout-row>.flex-xl-65,.layout-xl-row>.flex-xl-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:65%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-65,.layout-xl-column>.flex-xl-65{-webkit-flex:1 1 65%;-ms-flex:1 1 65%;flex:1 1 65%;max-width:100%;max-height:65%;box-sizing:border-box}.flex-xl-70,.layout-row>.flex-xl-70,.layout-xl-row>.flex-xl-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:70%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-70,.layout-xl-column>.flex-xl-70{-webkit-flex:1 1 70%;-ms-flex:1 1 70%;flex:1 1 70%;max-width:100%;max-height:70%;box-sizing:border-box}.flex-xl-75,.layout-row>.flex-xl-75,.layout-xl-row>.flex-xl-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:75%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-75,.layout-xl-column>.flex-xl-75{-webkit-flex:1 1 75%;-ms-flex:1 1 75%;flex:1 1 75%;max-width:100%;max-height:75%;box-sizing:border-box}.flex-xl-80,.layout-row>.flex-xl-80,.layout-xl-row>.flex-xl-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:80%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-80,.layout-xl-column>.flex-xl-80{-webkit-flex:1 1 80%;-ms-flex:1 1 80%;flex:1 1 80%;max-width:100%;max-height:80%;box-sizing:border-box}.flex-xl-85,.layout-row>.flex-xl-85,.layout-xl-row>.flex-xl-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:85%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-85,.layout-xl-column>.flex-xl-85{-webkit-flex:1 1 85%;-ms-flex:1 1 85%;flex:1 1 85%;max-width:100%;max-height:85%;box-sizing:border-box}.flex-xl-90,.layout-row>.flex-xl-90,.layout-xl-row>.flex-xl-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:90%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-90,.layout-xl-column>.flex-xl-90{-webkit-flex:1 1 90%;-ms-flex:1 1 90%;flex:1 1 90%;max-width:100%;max-height:90%;box-sizing:border-box}.flex-xl-95,.layout-row>.flex-xl-95,.layout-xl-row>.flex-xl-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:95%;max-height:100%;box-sizing:border-box}.layout-column>.flex-xl-95,.layout-xl-column>.flex-xl-95{-webkit-flex:1 1 95%;-ms-flex:1 1 95%;flex:1 1 95%;max-width:100%;max-height:95%;box-sizing:border-box}.flex-xl-100,.layout-column>.flex-xl-100,.layout-row>.flex-xl-100,.layout-xl-column>.flex-xl-100,.layout-xl-row>.flex-xl-100{-webkit-flex:1 1 100%;-ms-flex:1 1 100%;flex:1 1 100%;max-width:100%;max-height:100%;box-sizing:border-box}.layout-row>.flex-xl-33,.layout-xl-row>.flex-xl-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:33.33%;max-height:100%;box-sizing:border-box}.layout-row>.flex-xl-66,.layout-xl-row>.flex-xl-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:66.66%;max-height:100%;box-sizing:border-box}.layout-row>.flex,.layout-xl-row>.flex{min-width:0}.layout-column>.flex-xl-33,.layout-xl-column>.flex-xl-33{-webkit-flex:1 1 33.33%;-ms-flex:1 1 33.33%;flex:1 1 33.33%;max-width:100%;max-height:33.33%;box-sizing:border-box}.layout-column>.flex-xl-66,.layout-xl-column>.flex-xl-66{-webkit-flex:1 1 66.66%;-ms-flex:1 1 66.66%;flex:1 1 66.66%;max-width:100%;max-height:66.66%;box-sizing:border-box}.layout-column>.flex,.layout-xl-column>.flex{min-height:0}.layout-xl,.layout-xl-column,.layout-xl-row{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.layout-xl-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.layout-xl-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.hide-gt-lg:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show),.hide-gt-md:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show),.hide-gt-sm:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show),.hide-gt-xs:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show),.hide-xl:not(.show-xl):not(.show-gt-lg):not(.show-gt-md):not(.show-gt-sm):not(.show-gt-xs):not(.show),.hide:not(.show-gt-xs):not(.show-gt-sm):not(.show-gt-md):not(.show-gt-lg):not(.show-xl):not(.show){display:none}}@media print{.hide-print:not(.show-print):not(.show){display:none!important}} \ No newline at end of file diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.js new file mode 100644 index 0000000000000000000000000000000000000000..b9ad5a213d19076a47cf1dc2d4ea56478562d6ff --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-material/angular-material.min.js @@ -0,0 +1,17 @@ +/*! + * Angular Material Design + * https://github.com/angular/material + * @license MIT + * v1.1.0-rc.5 + */ +!function(e,t,n){"use strict";!function(){t.module("ngMaterial",["ng","ngAnimate","ngAria","material.core","material.core.gestures","material.core.layout","material.core.theming.palette","material.core.theming","material.core.animate","material.components.autocomplete","material.components.backdrop","material.components.button","material.components.bottomSheet","material.components.checkbox","material.components.chips","material.components.card","material.components.colors","material.components.content","material.components.datepicker","material.components.dialog","material.components.divider","material.components.fabActions","material.components.fabShared","material.components.fabSpeedDial","material.components.fabToolbar","material.components.fabTrigger","material.components.icon","material.components.gridList","material.components.input","material.components.list","material.components.menu","material.components.menuBar","material.components.navBar","material.components.panel","material.components.progressLinear","material.components.progressCircular","material.components.radioButton","material.components.select","material.components.showHide","material.components.sidenav","material.components.slider","material.components.sticky","material.components.subheader","material.components.swipe","material.components.switch","material.components.tabs","material.components.toast","material.components.tooltip","material.components.virtualRepeat","material.components.toolbar","material.components.whiteframe"])}(),function(){function e(e,t){if(t.has("$swipe")){var n="You are using the ngTouch module. \nAngular Material already has mobile click, tap, and swipe support... \nngTouch is not supported with Angular Material!";e.warn(n)}}function n(e,t){e.decorator("$$rAF",["$delegate",o]),t.theme("default").primaryPalette("indigo").accentPalette("pink").warnPalette("deep-orange").backgroundPalette("grey")}function o(e){return e.throttle=function(t){var n,o,i,r;return function(){n=arguments,r=this,i=t,o||(o=!0,e(function(){i.apply(r,Array.prototype.slice.call(n)),o=!1}))}},e}t.module("material.core",["ngAnimate","material.core.animate","material.core.layout","material.core.gestures","material.core.theming"]).config(n).run(e),e.$inject=["$log","$injector"],n.$inject=["$provide","$mdThemingProvider"],o.$inject=["$delegate"]}(),function(){function e(){return{restrict:"A",link:n}}function n(e,t,n){var o=n.mdAutoFocus||n.mdAutofocus||n.mdSidenavFocus;e.$watch(o,function(e){t.toggleClass("_md-autofocus",e)})}t.module("material.core").directive("mdAutofocus",e).directive("mdAutoFocus",e).directive("mdSidenavFocus",e)}(),function(){function e(){function e(e){var t="#"===e[0]?e.substr(1):e,n=t.length/3,o=t.substr(0,n),i=t.substr(n,n),r=t.substr(2*n);return 1===n&&(o+=o,i+=i,r+=r),"rgba("+parseInt(o,16)+","+parseInt(i,16)+","+parseInt(r,16)+",0.1)"}function t(e){e=e.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);var t=e&&4===e.length?"#"+("0"+parseInt(e[1],10).toString(16)).slice(-2)+("0"+parseInt(e[2],10).toString(16)).slice(-2)+("0"+parseInt(e[3],10).toString(16)).slice(-2):"";return t.toUpperCase()}function n(e){return e.replace(")",", 0.1)").replace("(","a(")}function o(e){return e?e.replace("rgba","rgb").replace(/,[^\),]+\)/,")"):"rgb(0,0,0)"}return{rgbaToHex:t,hexToRgba:e,rgbToRgba:n,rgbaToRgb:o}}t.module("material.core").factory("$mdColorUtil",e)}(),function(){function e(e){function n(e){var t=r+"-"+e,n=i(t),a=n.charAt(0).toLowerCase()+n.substring(1);return o(e)?e:o(n)?n:o(a)?a:e}function o(e){return t.isDefined(s.style[e])}function i(e){return e.replace(d,function(e,t,n,o){return o?n.toUpperCase():n})}var r=e.vendorPrefix,a=/webkit/i.test(r),d=/([:\-_]+(.))/g,s=document.createElement("div");return{KEY_CODE:{COMMA:188,SEMICOLON:186,ENTER:13,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,TAB:9,BACKSPACE:8,DELETE:46},CSS:{TRANSITIONEND:"transitionend"+(a?" webkitTransitionEnd":""),ANIMATIONEND:"animationend"+(a?" webkitAnimationEnd":""),TRANSFORM:n("transform"),TRANSFORM_ORIGIN:n("transformOrigin"),TRANSITION:n("transition"),TRANSITION_DURATION:n("transitionDuration"),ANIMATION_PLAY_STATE:n("animationPlayState"),ANIMATION_DURATION:n("animationDuration"),ANIMATION_NAME:n("animationName"),ANIMATION_TIMING:n("animationTimingFunction"),ANIMATION_DIRECTION:n("animationDirection")},MEDIA:{xs:"(max-width: 599px)","gt-xs":"(min-width: 600px)",sm:"(min-width: 600px) and (max-width: 959px)","gt-sm":"(min-width: 960px)",md:"(min-width: 960px) and (max-width: 1279px)","gt-md":"(min-width: 1280px)",lg:"(min-width: 1280px) and (max-width: 1919px)","gt-lg":"(min-width: 1920px)",xl:"(min-width: 1920px)",landscape:"(orientation: landscape)",portrait:"(orientation: portrait)",print:"print"},MEDIA_PRIORITY:["xl","gt-lg","lg","gt-md","md","gt-sm","sm","gt-xs","xs","landscape","portrait","print"]}}t.module("material.core").factory("$mdConstant",e),e.$inject=["$sniffer"]}(),function(){function e(e,n){function o(){return[].concat(v)}function i(){return v.length}function r(e){return v.length&&e>-1&&e<v.length}function a(e){return e?r(u(e)+1):!1}function d(e){return e?r(u(e)-1):!1}function s(e){return r(e)?v[e]:null}function c(e,t){return v.filter(function(n){return n[e]===t})}function l(e,n){return e?(t.isNumber(n)||(n=v.length),v.splice(n,0,e),u(e)):-1}function m(e){h(e)&&v.splice(u(e),1)}function u(e){return v.indexOf(e)}function h(e){return e&&u(e)>-1}function p(){return v.length?v[0]:null}function f(){return v.length?v[v.length-1]:null}function g(e,o,i,a){i=i||b;for(var d=u(o);;){if(!r(d))return null;var s=d+(e?-1:1),c=null;if(r(s)?c=v[s]:n&&(c=e?f():p(),s=u(c)),null===c||s===a)return null;if(i(c))return c;t.isUndefined(a)&&(a=s),d=s}}var b=function(){return!0};e&&!t.isArray(e)&&(e=Array.prototype.slice.call(e)),n=!!n;var v=e||[];return{items:o,count:i,inRange:r,contains:h,indexOf:u,itemAt:s,findBy:c,add:l,remove:m,first:p,last:f,next:t.bind(null,g,!1),previous:t.bind(null,g,!0),hasPrevious:d,hasNext:a}}t.module("material.core").config(["$provide",function(t){t.decorator("$mdUtil",["$delegate",function(t){return t.iterator=e,t}])}])}(),function(){function e(e,n,o){function i(e){var n=u[e];t.isUndefined(n)&&(n=u[e]=r(e));var o=p[n];return t.isUndefined(o)&&(o=a(n)),o}function r(t){return e.MEDIA[t]||("("!==t.charAt(0)?"("+t+")":t)}function a(e){var t=h[e];return t||(t=h[e]=o.matchMedia(e)),t.addListener(d),p[t.media]=!!t.matches}function d(e){n.$evalAsync(function(){p[e.media]=!!e.matches})}function s(e){return h[e]}function c(t,n){for(var o=0;o<e.MEDIA_PRIORITY.length;o++){var i=e.MEDIA_PRIORITY[o];if(h[u[i]].matches){var r=m(t,n+"-"+i);if(t[r])return t[r]}}return t[m(t,n)]}function l(n,o,i){var r=[];return n.forEach(function(n){var a=m(o,n);t.isDefined(o[a])&&r.push(o.$observe(a,t.bind(void 0,i,null)));for(var d in e.MEDIA)a=m(o,n+"-"+d),t.isDefined(o[a])&&r.push(o.$observe(a,t.bind(void 0,i,d)))}),function(){r.forEach(function(e){e()})}}function m(e,t){return f[t]||(f[t]=e.$normalize(t))}var u={},h={},p={},f={};return i.getResponsiveAttribute=c,i.getQuery=s,i.watchResponsiveAttributes=l,i}t.module("material.core").factory("$mdMedia",e),e.$inject=["$mdConstant","$rootScope","$window"]}(),function(){function e(e,n){function o(e){return e=t.isArray(e)?e:[e],e.forEach(function(t){a.forEach(function(n){e.push(n+"-"+t)})}),e}function i(e){return e=t.isArray(e)?e:[e],o(e).map(function(e){return"["+e+"]"}).join(",")}function r(e,t){e=e[0]||e;for(var n=o(t),i=0;i<n.length;i++)if(e.hasAttribute(n[i]))return!0;return!1}var a=["data","x"];return e?n?i(e):o(e):{buildList:o,buildSelector:i,hasAttribute:r}}t.module("material.core").config(["$provide",function(t){t.decorator("$mdUtil",["$delegate",function(t){return t.prefixer=e,t}])}])}(),function(){function o(o,r,a,d,s,c,l,m,u){function h(e){return e[0]||e}var p=c.startSymbol(),f=c.endSymbol(),g="{{"===p&&"}}"===f,b=function(e,n,o){var i=!1;if(e&&e.length){var r=u.getComputedStyle(e[0]);i=t.isDefined(r[n])&&(o?r[n]==o:!0)}return i},v={dom:{},now:e.performance?t.bind(e.performance,e.performance.now):Date.now||function(){return(new Date).getTime()},bidi:function(e,n,i,r){function a(e){return e?d(e)?e:e+"px":"0"}function d(e){return String(e).indexOf("px")>-1}var s=!("rtl"==o[0].dir||"rtl"==o[0].body.dir);return 0==arguments.length?s?"ltr":"rtl":void(s&&t.isDefined(i)?t.element(e).css(n,a(i)):!s&&t.isDefined(r)&&t.element(e).css(n,a(r)))},clientRect:function(e,t,n){var o=h(e);t=h(t||o.offsetParent||document.body);var i=o.getBoundingClientRect(),r=n?t.getBoundingClientRect():{left:0,top:0,width:0,height:0};return{left:i.left-r.left,top:i.top-r.top,width:i.width,height:i.height}},offsetRect:function(e,t){return v.clientRect(e,t,!0)},nodesToArray:function(e){e=e||[];for(var t=[],n=0;n<e.length;++n)t.push(e.item(n));return t},scrollTop:function(e){e=t.element(e||o[0].body);var i=e[0]==o[0].body?o[0].body:n,r=i?i.scrollTop+i.parentElement.scrollTop:0;return r||Math.abs(e[0].getBoundingClientRect().top)},findFocusTarget:function(e,n){function o(e,n){var o,i=e[0].querySelectorAll(n);return i&&i.length&&i.length&&t.forEach(i,function(e){e=t.element(e);var n=e.hasClass("_md-autofocus");n&&(o=e)}),o}var i,r=this.prefixer("md-autofocus",!0);return i=o(e,n||r),i||n==r||(i=o(e,this.prefixer("md-auto-focus",!0)),i||(i=o(e,r))),i},disableScrollAround:function(e,n){function i(e){function n(e){e.preventDefault()}e=t.element(e||d)[0];var o=t.element('<div class="md-scroll-mask"> <div class="md-scroll-mask-bar"></div></div>');return e.appendChild(o[0]),o.on("wheel",n),o.on("touchmove",n),function(){o.off("wheel"),o.off("touchmove"),o[0].parentNode.removeChild(o[0]),delete v.disableScrollAround._enableScrolling}}function r(){var e=d.parentNode,t=e.style.cssText||"",n=d.style.cssText||"",o=v.scrollTop(d),i=d.clientWidth;return d.scrollHeight>d.clientHeight+1&&(a(d,{position:"fixed",width:"100%",top:-o+"px"}),e.style.overflowY="scroll"),d.clientWidth<i&&a(d,{overflow:"hidden"}),function(){d.style.cssText=n,e.style.cssText=t,d.scrollTop=o,e.scrollTop=o}}function a(e,t){for(var n in t)e.style[n]=t[n]}if(v.disableScrollAround._count=v.disableScrollAround._count||0,++v.disableScrollAround._count,v.disableScrollAround._enableScrolling)return v.disableScrollAround._enableScrolling;var d=o[0].body,s=r(),c=i(n);return v.disableScrollAround._enableScrolling=function(){--v.disableScrollAround._count||(s(),c(),delete v.disableScrollAround._enableScrolling)}},enableScrolling:function(){var e=this.disableScrollAround._enableScrolling;e&&e()},floatingScrollbars:function(){if(this.floatingScrollbars.cached===n){var e=t.element("<div><div></div></div>").css({width:"100%","z-index":-1,position:"absolute",height:"35px","overflow-y":"scroll"});e.children().css("height","60px"),o[0].body.appendChild(e[0]),this.floatingScrollbars.cached=e[0].offsetWidth==e[0].childNodes[0].offsetWidth,e.remove()}return this.floatingScrollbars.cached},forceFocus:function(t){var n=t[0]||t;document.addEventListener("click",function i(e){e.target===n&&e.$focus&&(n.focus(),e.stopImmediatePropagation(),e.preventDefault(),n.removeEventListener("click",i))},!0);var o=document.createEvent("MouseEvents");o.initMouseEvent("click",!1,!0,e,{},0,0,0,0,!1,!1,!1,!1,0,null),o.$material=!0,o.$focus=!0,n.dispatchEvent(o)},createBackdrop:function(e,t){return a(v.supplant('<md-backdrop class="{0}">',[t]))(e)},supplant:function(e,t,n){return n=n||/\{([^\{\}]*)\}/g,e.replace(n,function(e,n){var o=n.split("."),i=t;try{for(var r in o)o.hasOwnProperty(r)&&(i=i[o[r]])}catch(a){i=e}return"string"==typeof i||"number"==typeof i?i:e})},fakeNgModel:function(){return{$fake:!0,$setTouched:t.noop,$setViewValue:function(e){this.$viewValue=e,this.$render(e),this.$viewChangeListeners.forEach(function(e){e()})},$isEmpty:function(e){return 0===(""+e).length},$parsers:[],$formatters:[],$viewChangeListeners:[],$render:t.noop}},debounce:function(e,t,o,i){var a;return function(){var d=o,s=Array.prototype.slice.call(arguments);r.cancel(a),a=r(function(){a=n,e.apply(d,s)},t||10,i)}},throttle:function(e,t){var n;return function(){var o=this,i=arguments,r=v.now();(!n||r-n>t)&&(e.apply(o,i),n=r)}},time:function(e){var t=v.now();return e(),v.now()-t},valueOnUse:function(e,t,n){var o=null,i=Array.prototype.slice.call(arguments),r=i.length>3?i.slice(3):[];Object.defineProperty(e,t,{get:function(){return null===o&&(o=n.apply(e,r)),o}})},nextUid:function(){return""+i++},disconnectScope:function(e){if(e&&e.$root!==e&&!e.$$destroyed){var t=e.$parent;e.$$disconnected=!0,t.$$childHead===e&&(t.$$childHead=e.$$nextSibling),t.$$childTail===e&&(t.$$childTail=e.$$prevSibling),e.$$prevSibling&&(e.$$prevSibling.$$nextSibling=e.$$nextSibling),e.$$nextSibling&&(e.$$nextSibling.$$prevSibling=e.$$prevSibling),e.$$nextSibling=e.$$prevSibling=null}},reconnectScope:function(e){if(e&&e.$root!==e&&e.$$disconnected){var t=e,n=t.$parent;t.$$disconnected=!1,t.$$prevSibling=n.$$childTail,n.$$childHead?(n.$$childTail.$$nextSibling=t,n.$$childTail=t):n.$$childHead=n.$$childTail=t}},getClosest:function(e,n,o){if(e instanceof t.element&&(e=e[0]),n=n.toUpperCase(),o&&(e=e.parentNode),!e)return null;do if(e.nodeName===n)return e;while(e=e.parentNode);return null},elementContains:function(n,o){var i=e.Node&&e.Node.prototype&&Node.prototype.contains,r=i?t.bind(n,n.contains):t.bind(n,function(e){return n===o||!!(16&this.compareDocumentPosition(e))});return r(o)},extractElementByName:function(e,n,o,i){function r(e){return a(e)||(o?d(e):null)}function a(e){if(e)for(var t=0,o=e.length;o>t;t++)if(e[t].nodeName.toLowerCase()===n)return e[t];return null}function d(e){var t;if(e)for(var n=0,o=e.length;o>n;n++){var i=e[n];if(!t)for(var a=0,d=i.childNodes.length;d>a;a++)t=t||r([i.childNodes[a]])}return t}var s=r(e);return!s&&i&&l.warn(v.supplant("Unable to find node '{0}' in element '{1}'.",[n,e[0].outerHTML])),t.element(s||e)},initOptionalProperties:function(e,n,o){o=o||{},t.forEach(e.$$isolateBindings,function(i,r){if(i.optional&&t.isUndefined(e[r])){var a=t.isDefined(n[i.attrName]);e[r]=t.isDefined(o[r])?o[r]:a}})},nextTick:function(e,t,n){function o(){var e=i.queue,t=i.digest;i.queue=[],i.timeout=null,i.digest=!1,e.forEach(function(e){var t=e.scope&&e.scope.$$destroyed;t||e.callback()}),t&&d.$digest()}var i=v.nextTick,a=i.timeout,s=i.queue||[];return s.push({scope:n,callback:e}),null==t&&(t=!0),i.digest=i.digest||t,i.queue=s,a||(i.timeout=r(o,0,!1))},processTemplate:function(e){return g?e:e&&t.isString(e)?e.replace(/\{\{/g,p).replace(/}}/g,f):e},getParentWithPointerEvents:function(e){for(var t=e.parent();b(t,"pointer-events","none");)t=t.parent();return t},getNearestContentElement:function(e){for(var t=e.parent()[0];t&&t!==m[0]&&t!==document.body&&"MD-CONTENT"!==t.nodeName.toUpperCase();)t=t.parentNode;return t},parseAttributeBoolean:function(e,t){return""===e||!!e&&(t===!1||"false"!==e&&"0"!==e)},hasComputedStyle:b};return v.dom.animator=s(v),v}var i=0;t.module("material.core").factory("$mdUtil",o),o.$inject=["$document","$timeout","$compile","$rootScope","$$mdAnimate","$interpolate","$log","$rootElement","$window"],t.element.prototype.focus=t.element.prototype.focus||function(){return this.length&&this[0].focus(),this},t.element.prototype.blur=t.element.prototype.blur||function(){return this.length&&this[0].blur(),this}}(),function(){function e(e,n,o,i){function r(e,o,i){var r=t.element(e)[0]||e;!r||r.hasAttribute(o)&&0!==r.getAttribute(o).length||c(r,o)||(i=t.isString(i)?i.trim():"",i.length?e.attr(o,i):n.warn('ARIA: Attribute "',o,'", required for accessibility, is missing on node:',r))}function a(t,n,o){e(function(){r(t,n,o())})}function d(e,t){var n=s(e)||"",o=n.indexOf(i.startSymbol())>-1;o?a(e,t,function(){return s(e)}):r(e,t,n)}function s(e){function t(t){for(;t.parentNode&&(t=t.parentNode)!==e;)if(t.getAttribute&&"true"===t.getAttribute("aria-hidden"))return!0}e=e[0]||e;for(var n,o=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null,!1),i="";n=o.nextNode();)t(n)||(i+=n.textContent);return i.trim()||""}function c(e,t){function n(e){var t=e.currentStyle?e.currentStyle:o.getComputedStyle(e);return"none"===t.display}var i=e.hasChildNodes(),r=!1;if(i)for(var a=e.childNodes,d=0;d<a.length;d++){var s=a[d];1===s.nodeType&&s.hasAttribute(t)&&(n(s)||(r=!0))}return r}return{expect:r,expectAsync:a,expectWithText:d}}t.module("material.core").service("$mdAria",e),e.$inject=["$$rAF","$log","$window","$interpolate"]}(),function(){function e(e,n,o,i,r){this.compile=function(a){var d=a.templateUrl,s=a.template||"",c=a.controller,l=a.controllerAs,m=t.extend({},a.resolve||{}),u=t.extend({},a.locals||{}),h=a.transformTemplate||t.identity,p=a.bindToController;return t.forEach(m,function(e,n){t.isString(e)?m[n]=o.get(e):m[n]=o.invoke(e)}),t.extend(m,u),d?m.$template=n(d).then(function(e){return e}):m.$template=e.when(s),e.all(m).then(function(e){var n,o=h(e.$template,a),d=a.element||t.element("<div>").html(o.trim()).contents(),s=i(d);return n={locals:e,element:d,link:function(o){if(e.$scope=o,c){var i=r(c,e,!0);p&&t.extend(i.instance,e);var a=i();d.data("$ngControllerController",a),d.children().data("$ngControllerController",a),l&&(o[l]=a),n.controller=a}return s(o)}}})}}t.module("material.core").service("$mdCompiler",e),e.$inject=["$q","$templateRequest","$injector","$compile","$controller"]}(),function(){function n(){}function o(n,o,i){function r(e){return function(t,n){n.distance<this.state.options.maxDistance&&this.dispatchEvent(t,e,n)}}function a(e,t,n){var o=p[t.replace(/^\$md./,"")];if(!o)throw new Error("Failed to register element with handler "+t+". Available handlers: "+Object.keys(p).join(", "));return o.registerElement(e,n)}function s(e,o){var i=new n(e);return t.extend(i,o),p[e]=i,g}var c=navigator.userAgent||navigator.vendor||e.opera,m=c.match(/ipad|iphone|ipod/i),u=c.match(/android/i),h="undefined"!=typeof e.jQuery&&t.element===e.jQuery,g={handler:s,register:a,isHijackingClicks:(m||u)&&!h&&!f};if(g.isHijackingClicks){var b=6;g.handler("click",{options:{maxDistance:b},onEnd:r("click")}),g.handler("focus",{options:{maxDistance:b},onEnd:function(e,t){function n(e){var t=["INPUT","SELECT","BUTTON","TEXTAREA","VIDEO","AUDIO"];return"-1"!=e.getAttribute("tabindex")&&!e.hasAttribute("DISABLED")&&(e.hasAttribute("tabindex")||e.hasAttribute("href")||-1!=t.indexOf(e.nodeName))}t.distance<this.state.options.maxDistance&&n(e.target)&&(this.dispatchEvent(e,"focus",t),e.target.focus())}}),g.handler("mouseup",{options:{maxDistance:b},onEnd:r("mouseup")}),g.handler("mousedown",{onStart:function(e){this.dispatchEvent(e,"mousedown")}})}return g.handler("press",{onStart:function(e,t){this.dispatchEvent(e,"$md.pressdown")},onEnd:function(e,t){this.dispatchEvent(e,"$md.pressup")}}).handler("hold",{options:{maxDistance:6,delay:500},onCancel:function(){i.cancel(this.state.timeout)},onStart:function(e,n){return this.state.registeredParent?(this.state.pos={x:n.x,y:n.y},void(this.state.timeout=i(t.bind(this,function(){this.dispatchEvent(e,"$md.hold"),this.cancel()}),this.state.options.delay,!1))):this.cancel()},onMove:function(e,t){e.preventDefault();var n=this.state.pos.x-t.x,o=this.state.pos.y-t.y;Math.sqrt(n*n+o*o)>this.options.maxDistance&&this.cancel()},onEnd:function(){this.onCancel()}}).handler("drag",{options:{minDistance:6,horizontal:!0,cancelMultiplier:1.5},onStart:function(e){this.state.registeredParent||this.cancel()},onMove:function(e,t){var n,o;e.preventDefault(),this.state.dragPointer?this.dispatchDragMove(e):(this.state.options.horizontal?(n=Math.abs(t.distanceX)>this.state.options.minDistance,o=Math.abs(t.distanceY)>this.state.options.minDistance*this.state.options.cancelMultiplier):(n=Math.abs(t.distanceY)>this.state.options.minDistance,o=Math.abs(t.distanceX)>this.state.options.minDistance*this.state.options.cancelMultiplier),n?(this.state.dragPointer=d(e),l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.dragstart",this.state.dragPointer)):o&&this.cancel())},dispatchDragMove:o.throttle(function(e){this.state.isRunning&&(l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.drag",this.state.dragPointer))}),onEnd:function(e,t){this.state.dragPointer&&(l(e,this.state.dragPointer),this.dispatchEvent(e,"$md.dragend",this.state.dragPointer))}}).handler("swipe",{options:{minVelocity:.65,minDistance:10},onEnd:function(e,t){var n;Math.abs(t.velocityX)>this.state.options.minVelocity&&Math.abs(t.distanceX)>this.state.options.minDistance?(n="left"==t.directionX?"$md.swipeleft":"$md.swiperight",this.dispatchEvent(e,n)):Math.abs(t.velocityY)>this.state.options.minVelocity&&Math.abs(t.distanceY)>this.state.options.minDistance&&(n="up"==t.directionY?"$md.swipeup":"$md.swipedown",this.dispatchEvent(e,n))}})}function i(e){this.name=e,this.state={}}function r(){function n(e,n,o){o=o||u;var i=new t.element.Event(n);i.$material=!0,i.pointer=o,i.srcEvent=e,t.extend(i,{clientX:o.x,clientY:o.y,screenX:o.x,screenY:o.y,pageX:o.x,pageY:o.y,ctrlKey:e.ctrlKey,altKey:e.altKey,shiftKey:e.shiftKey,metaKey:e.metaKey}),t.element(o.target).trigger(i)}function o(t,n,o){o=o||u;var i;"click"===n||"mouseup"==n||"mousedown"==n?(i=document.createEvent("MouseEvents"),i.initMouseEvent(n,!0,!0,e,t.detail,o.x,o.y,o.x,o.y,t.ctrlKey,t.altKey,t.shiftKey,t.metaKey,t.button,t.relatedTarget||null)):(i=document.createEvent("CustomEvent"),i.initCustomEvent(n,!0,!0,{})),i.$material=!0,i.pointer=o,i.srcEvent=t,o.target.dispatchEvent(i)}var r="undefined"!=typeof e.jQuery&&t.element===e.jQuery;return i.prototype={options:{},dispatchEvent:r?n:o,onStart:t.noop,onMove:t.noop,onEnd:t.noop,onCancel:t.noop,start:function(e,n){if(!this.state.isRunning){var o=this.getNearestParent(e.target),i=o&&o.$mdGesture[this.name]||{};this.state={isRunning:!0,options:t.extend({},this.options,i),registeredParent:o},this.onStart(e,n)}},move:function(e,t){this.state.isRunning&&this.onMove(e,t)},end:function(e,t){this.state.isRunning&&(this.onEnd(e,t),this.state.isRunning=!1)},cancel:function(e,t){this.onCancel(e,t),this.state={}},getNearestParent:function(e){for(var t=e;t;){if((t.$mdGesture||{})[this.name])return t;t=t.parentNode}return null},registerElement:function(e,t){function n(){delete e[0].$mdGesture[o.name],e.off("$destroy",n)}var o=this;return e[0].$mdGesture=e[0].$mdGesture||{},e[0].$mdGesture[this.name]=t||{},e.on("$destroy",n),n}},i}function a(e,n){function o(e){var t=!e.clientX&&!e.clientY;t||e.$material||e.isIonicTap||c(e)||(e.preventDefault(),e.stopPropagation())}function i(e){var t=0===e.clientX&&0===e.clientY;t||e.$material||e.isIonicTap||c(e)?(g=null,"label"==e.target.tagName.toLowerCase()&&(g={x:e.x,y:e.y})):(e.preventDefault(),e.stopPropagation(),g=null)}function r(e,t){var o;for(var i in p)o=p[i],o instanceof n&&("start"===e&&o.cancel(),o[e](t,u))}function a(e){if(!u){var t=+Date.now();h&&!s(e,h)&&t-h.endTime<1500||(u=d(e),r("start",e))}}function m(e){u&&s(e,u)&&(l(e,u),r("move",e))}function f(e){u&&s(e,u)&&(l(e,u),u.endTime=+Date.now(),r("end",e),h=u,u=null)}document.contains||(document.contains=function(e){return document.body.contains(e)}),!b&&e.isHijackingClicks&&(document.addEventListener("click",i,!0),document.addEventListener("mouseup",o,!0),document.addEventListener("mousedown",o,!0),document.addEventListener("focus",o,!0),b=!0);var v="mousedown touchstart pointerdown",E="mousemove touchmove pointermove",$="mouseup mouseleave touchend touchcancel pointerup pointercancel";t.element(document).on(v,a).on(E,m).on($,f).on("$$mdGestureReset",function(){h=u=null})}function d(e){var t=m(e),n={startTime:+Date.now(),target:e.target,type:e.type.charAt(0)};return n.startX=n.x=t.pageX,n.startY=n.y=t.pageY,n}function s(e,t){return e&&t&&e.type.charAt(0)===t.type}function c(e){return g&&g.x==e.x&&g.y==e.y}function l(e,t){var n=m(e),o=t.x=n.pageX,i=t.y=n.pageY;t.distanceX=o-t.startX,t.distanceY=i-t.startY,t.distance=Math.sqrt(t.distanceX*t.distanceX+t.distanceY*t.distanceY),t.directionX=t.distanceX>0?"right":t.distanceX<0?"left":"",t.directionY=t.distanceY>0?"down":t.distanceY<0?"up":"",t.duration=+Date.now()-t.startTime,t.velocityX=t.distanceX/t.duration,t.velocityY=t.distanceY/t.duration}function m(e){return e=e.originalEvent||e,e.touches&&e.touches[0]||e.changedTouches&&e.changedTouches[0]||e}var u,h,p={},f=!1,g=null,b=!1;t.module("material.core.gestures",[]).provider("$mdGesture",n).factory("$$MdGestureHandler",r).run(a),n.prototype={skipClickHijack:function(){return f=!0},$get:["$$MdGestureHandler","$$rAF","$timeout",function(e,t,n){return new o(e,t,n)}]},o.$inject=["$$MdGestureHandler","$$rAF","$timeout"],a.$inject=["$mdGesture","$$MdGestureHandler"]}(),function(){function e(){function e(e){function n(e){return s.optionsFactory=e.options,s.methods=(e.methods||[]).concat(a),c}function o(e,t){return d[e]=t,c}function i(t,n){if(n=n||{},n.methods=n.methods||[],n.options=n.options||function(){return{}},/^cancel|hide|show$/.test(t))throw new Error("Preset '"+t+"' in "+e+" is reserved!");if(n.methods.indexOf("_options")>-1)throw new Error("Method '_options' in "+e+" is reserved!");return s.presets[t]={methods:n.methods.concat(a),optionsFactory:n.options,argOption:n.argOption},c}function r(n,o){function i(e){return e=e||{},e._options&&(e=e._options),m.show(t.extend({},l,e))}function r(e){return m.destroy(e)}function a(t,n){var i={};return i[e]=u,o.invoke(t||function(){return n},{},i)}var c,l,m=n(),u={hide:m.hide,cancel:m.cancel,show:i,destroy:r};return c=s.methods||[],l=a(s.optionsFactory,{}),t.forEach(d,function(e,t){u[t]=e}),t.forEach(s.presets,function(e,n){function o(e){this._options=t.extend({},i,e)}var i=a(e.optionsFactory,{}),r=(e.methods||[]).concat(c);if(t.extend(i,{$type:n}),t.forEach(r,function(e){o.prototype[e]=function(t){return this._options[e]=t,this}}),e.argOption){var d="show"+n.charAt(0).toUpperCase()+n.slice(1);u[d]=function(e){var t=u[n](e);return u.show(t)}}u[n]=function(n){return arguments.length&&e.argOption&&!t.isObject(n)&&!t.isArray(n)?(new o)[e.argOption](n):new o(n)}}),u}var a=["onHide","onShow","onRemove"],d={},s={presets:{}},c={setDefaults:n,addPreset:i,addMethod:o,$get:r};return c.addPreset("build",{methods:["controller","controllerAs","resolve","template","templateUrl","themable","transformTemplate","parent"]}),r.$inject=["$$interimElement","$injector"],c}function o(e,o,i,r,a,d,s,c,l,m,u){return function(){function h(e){e=e||{};var t=new b(e||{}),n=!e.skipHide&&$.length?v.cancel():o.when(!0);return n["finally"](function(){$.push(t),t.show()["catch"](function(e){return e})}),t.deferred.promise}function p(e,t){function i(n){return n.remove(e,!1,t||{})["catch"](function(e){return e}),n.deferred.promise}if(!$.length)return o.when(e);if(t=t||{},t.closeAll){var r=o.all($.reverse().map(i));return $=[],r}if(t.closeTo!==n)return o.all($.splice(t.closeTo).map(i));var a=$.pop();return i(a)}function f(e,n){var i=$.pop();return i?(i.remove(e,!0,n||{})["catch"](function(e){return e}),i.deferred.promise["catch"](t.noop)):o.when(e)}function g(e){var n=e?null:$.shift(),i=t.element(e).length?t.element(e)[0].parentNode:null;if(i){var r=$.filter(function(e){var t=e.options.element[0];return t===i});r.length>0&&(n=r[0],$.splice($.indexOf(n),1))}return n?n.remove(E,!1,{$destroy:!0}):o.when(E)}function b(u){function h(){return o(function(e,t){function n(e){M.deferred.reject(e),t(e)}g(u).then(function(t){_=b(t,u),A=C(_,u,t.controller).then(e,n)},n)})}function p(e,n,i){function r(e){M.deferred.resolve(e)}function a(e){M.deferred.reject(e)}return _?(u=t.extend(u||{},i||{}),u.cancelAutoHide&&u.cancelAutoHide(),u.element.triggerHandler("$mdInterimElementRemove"),u.$destroy===!0?y(u.element,u).then(function(){n&&a(e)||r(e)}):(o.when(A)["finally"](function(){y(u.element,u).then(function(){n&&a(e)||r(e)},a)}),M.deferred.promise)):o.when(!1)}function f(e){return e=e||{},e.template&&(e.template=c.processTemplate(e.template)),t.extend({preserveScope:!1,cancelAutoHide:t.noop,scope:e.scope||r.$new(e.isolateScope),onShow:function(e,t,n){return s.enter(t,n.parent)},onRemove:function(e,t){return t&&s.leave(t)||o.when()}},e)}function g(e){var t=e.skipCompile?null:l.compile(e);return t||o(function(t){t({locals:{},link:function(){return e.element}})})}function b(e,n){t.extend(e.locals,n);var o=e.link(n.scope);return n.element=o,n.parent=E(o,n),n.themable&&m(o),o}function E(n,o){var i=o.parent;if(i=t.isFunction(i)?i(o.scope,n,o):t.isString(i)?t.element(e[0].querySelector(i)):t.element(i),!(i||{}).length){var r;return d[0]&&d[0].querySelector&&(r=d[0].querySelector(":not(svg) > body")),r||(r=d[0]),"#comment"==r.nodeName&&(r=e[0].body),t.element(r)}return i}function $(){var e,o=t.noop;u.hideDelay&&(e=a(v.hide,u.hideDelay),o=function(){a.cancel(e)}),u.cancelAutoHide=function(){o(),u.cancelAutoHide=n}}function C(e,n,i){var r=n.onShowing||t.noop,a=n.onComplete||t.noop;return r(n.scope,e,n,i),o(function(t,r){try{o.when(n.onShow(n.scope,e,n,i)).then(function(){a(n.scope,e,n),$(),t(e)},r)}catch(d){r(d.message)}})}function y(e,n){var o=n.onRemoving||t.noop;return i(function(t,r){try{var a=i.when(n.onRemove(n.scope,e,n)||!0);o(e,a),1==n.$destroy?t(e):a.then(function(){!n.preserveScope&&n.scope&&n.scope.$destroy(),t(e)},r)}catch(d){r(d)}})}var M,_,A=o.when(!0);return u=f(u),M={options:u,deferred:o.defer(),show:h,remove:p}}var v,E=!1,$=[];return v={show:h,hide:p,cancel:f,destroy:g,$injector_:u}}}return e.$get=o,o.$inject=["$document","$q","$$q","$rootScope","$timeout","$rootElement","$animate","$mdUtil","$mdCompiler","$mdTheming","$injector"],e}t.module("material.core").provider("$$interimElement",e)}(),function(){!function(){function e(e){function a(e){return e.replace(s,"").replace(c,function(e,t,n,o){return o?n.toUpperCase():n})}var s=/^((?:x|data)[\:\-_])/i,c=/([\:\-\_]+(.))/g,l=["","xs","gt-xs","sm","gt-sm","md","gt-md","lg","gt-lg","xl","print"],m=["layout","flex","flex-order","flex-offset","layout-align"],u=["show","hide","layout-padding","layout-margin"];t.forEach(l,function(n){t.forEach(m,function(t){var o=n?t+"-"+n:t;e.directive(a(o),i(o))}),t.forEach(u,function(t){var o=n?t+"-"+n:t;e.directive(a(o),r(o))})}),e.directive("mdLayoutCss",n).directive("ngCloak",o("ng-cloak")).directive("layoutWrap",r("layout-wrap")).directive("layoutNowrap",r("layout-nowrap")).directive("layoutNoWrap",r("layout-no-wrap")).directive("layoutFill",r("layout-fill")).directive("layoutLtMd",d("layout-lt-md",!0)).directive("layoutLtLg",d("layout-lt-lg",!0)).directive("flexLtMd",d("flex-lt-md",!0)).directive("flexLtLg",d("flex-lt-lg",!0)).directive("layoutAlignLtMd",d("layout-align-lt-md")).directive("layoutAlignLtLg",d("layout-align-lt-lg")).directive("flexOrderLtMd",d("flex-order-lt-md")).directive("flexOrderLtLg",d("flex-order-lt-lg")).directive("offsetLtMd",d("flex-offset-lt-md")).directive("offsetLtLg",d("flex-offset-lt-lg")).directive("hideLtMd",d("hide-lt-md")).directive("hideLtLg",d("hide-lt-lg")).directive("showLtMd",d("show-lt-md")).directive("showLtLg",d("show-lt-lg"))}function n(){return{restrict:"A",priority:"900",compile:function(e,n){return _.enabled=!1,t.noop}}}function o(e){return["$timeout",function(n){return{restrict:"A",priority:-10,compile:function(o){return _.enabled?(o.addClass(e),function(t,o){n(function(){o.removeClass(e)},10,!1)}):t.noop}}}]}function i(e){function n(t,n,o){var i=a(n,e,o),r=o.$observe(o.$normalize(e),i);i(u(e,o,"")),t.$on("$destroy",function(){r()})}return["$mdUtil","$interpolate","$log",function(o,i,r){return f=o,g=i,b=r,{restrict:"A",compile:function(o,i){var r;return _.enabled&&(s(e,i,o,b),c(e,u(e,i,""),l(o,e,i)),r=n),r||t.noop}}}]}function r(e){function n(t,n){n.addClass(e)}return["$mdUtil","$interpolate","$log",function(o,i,r){return f=o,g=i,b=r,{restrict:"A",compile:function(o,i){var r;return _.enabled&&(c(e,u(e,i,""),l(o,e,i)),n(null,o),r=n),r||t.noop}}}]}function a(e,n){var o;return function(i){var r=c(n,i||"");t.isDefined(r)&&(o&&e.removeClass(o),o=r?n+"-"+r.replace(E,"-"):n,e.addClass(o))}}function d(e){var n=e.split("-");return["$log",function(o){return o.warn(e+"has been deprecated. Please use a `"+n[0]+"-gt-<xxx>` variant."), +t.noop}]}function s(e,t,n,o){var i,r,a,d=n[0].nodeName.toLowerCase();switch(e.replace(v,"")){case"flex":"md-button"!=d&&"fieldset"!=d||(r="<"+d+" "+e+"></"+d+">",a="https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers",i="Markup '{0}' may not work as expected in IE Browsers. Consult '{1}' for details.",o.warn(f.supplant(i,[r,a])))}}function c(e,n,o){var i=n;if(!m(n)){switch(e.replace(v,"")){case"layout":h(n,C)||(n=C[0]);break;case"flex":h(n,$)||isNaN(n)&&(n="");break;case"flex-offset":case"flex-order":n&&!isNaN(+n)||(n="0");break;case"layout-align":var r=p(n);n=f.supplant("{main}-{cross}",r);break;case"layout-padding":case"layout-margin":case"layout-fill":case"layout-wrap":case"layout-nowrap":case"layout-nowrap":n=""}n!=i&&(o||t.noop)(n)}return n}function l(e,t,n){return function(e){m(e)||(n[n.$normalize(t)]=e)}}function m(e){return(e||"").indexOf(g.startSymbol())>-1}function u(e,t,n){var o=t.$normalize(e);return t[o]?t[o].replace(E,"-"):n||null}function h(e,t,n){e=n&&e?e.replace(E,n):e;var o=!1;return e&&t.forEach(function(t){t=n?t.replace(E,n):t,o=o||t===e}),o}function p(e){var t,n={main:"start",cross:"stretch"};return e=e||"",0!=e.indexOf("-")&&0!=e.indexOf(" ")||(e="none"+e),t=e.toLowerCase().trim().replace(E,"-").split("-"),t.length&&"space"===t[0]&&(t=[t[0]+"-"+t[1],t[2]]),t.length>0&&(n.main=t[0]||n.main),t.length>1&&(n.cross=t[1]||n.cross),y.indexOf(n.main)<0&&(n.main="start"),M.indexOf(n.cross)<0&&(n.cross="stretch"),n}var f,g,b,v=/(-gt)?-(sm|md|lg|print)/g,E=/\s+/g,$=["grow","initial","auto","none","noshrink","nogrow"],C=["row","column"],y=["","start","center","end","stretch","space-around","space-between"],M=["","start","center","end","stretch"],_={enabled:!0,breakpoints:[]};e(t.module("material.core.layout",["ng"]))}()}(),function(){function e(e,o){function i(e){return e&&""!==e}var r,a=[],d={};return r={notFoundError:function(t,n){e.error((n||"")+"No instance found for handle",t)},getInstances:function(){return a},get:function(e){if(!i(e))return null;var t,n,o;for(t=0,n=a.length;n>t;t++)if(o=a[t],o.$$mdHandle===e)return o;return null},register:function(e,n){function o(){var t=a.indexOf(e);-1!==t&&a.splice(t,1)}function i(){var t=d[n];t&&(t.forEach(function(t){t.resolve(e)}),delete d[n])}return n?(e.$$mdHandle=n,a.push(e),i(),o):t.noop},when:function(e){if(i(e)){var t=o.defer(),a=r.get(e);return a?t.resolve(a):(d[e]===n&&(d[e]=[]),d[e].push(t)),t.promise}return o.reject("Invalid `md-component-id` value.")}}}t.module("material.core").factory("$mdComponentRegistry",e),e.$inject=["$log","$q"]}(),function(){!function(){function e(e){function n(e){return e.hasClass("md-icon-button")?{isMenuItem:e.hasClass("md-menu-item"),fitRipple:!0,center:!0}:{isMenuItem:e.hasClass("md-menu-item"),dimBackground:!0}}return{attach:function(o,i,r){return r=t.extend(n(i),r),e.attach(o,i,r)}}}t.module("material.core").factory("$mdButtonInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){!function(){function e(e){function n(n,o,i){return e.attach(n,o,t.extend({center:!0,dimBackground:!1,fitRipple:!0},i))}return{attach:n}}t.module("material.core").factory("$mdCheckboxInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){!function(){function e(e){function n(n,o,i){return e.attach(n,o,t.extend({center:!1,dimBackground:!0,outline:!1,rippleSize:"full"},i))}return{attach:n}}t.module("material.core").factory("$mdListInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){function e(e,n){return{controller:t.noop,link:function(t,o,i){i.hasOwnProperty("mdInkRippleCheckbox")?n.attach(t,o):e.attach(t,o)}}}function n(){function e(){n=!0}var n=!1;return{disableInkRipple:e,$get:["$injector",function(e){function i(i,r,a){return n||r.controller("mdNoInk")?t.noop:e.instantiate(o,{$scope:i,$element:r,rippleOptions:a})}return{attach:i}}]}}function o(e,n,o,i,r,a,d){this.$window=i,this.$timeout=r,this.$mdUtil=a,this.$mdColorUtil=d,this.$scope=e,this.$element=n,this.options=o,this.mousedown=!1,this.ripples=[],this.timeout=null,this.lastRipple=null,a.valueOnUse(this,"container",this.createContainer),this.$element.addClass("md-ink-ripple"),(n.controller("mdInkRipple")||{}).createRipple=t.bind(this,this.createRipple),(n.controller("mdInkRipple")||{}).setColor=t.bind(this,this.color),this.bindEvents()}function i(e,n){(e.mousedown||e.lastRipple)&&(e.mousedown=!1,e.$mdUtil.nextTick(t.bind(e,n),!1))}function r(){return{controller:t.noop}}t.module("material.core").provider("$mdInkRipple",n).directive("mdInkRipple",e).directive("mdNoInk",r).directive("mdNoBar",r).directive("mdNoStretch",r);var a=450;e.$inject=["$mdButtonInkRipple","$mdCheckboxInkRipple"],o.$inject=["$scope","$element","rippleOptions","$window","$timeout","$mdUtil","$mdColorUtil"],o.prototype.color=function(e){function n(){var e=o.options&&o.options.colorElement?o.options.colorElement:[],t=e.length?e[0]:o.$element[0];return t?o.$window.getComputedStyle(t).color:"rgb(0,0,0)"}var o=this;return t.isDefined(e)&&(o._color=o._parseColor(e)),o._color||o._parseColor(o.inkRipple())||o._parseColor(n())},o.prototype.calculateColor=function(){return this.color()},o.prototype._parseColor=function(e,t){t=t||1;var n=this.$mdColorUtil;if(e)return 0===e.indexOf("rgba")?e.replace(/\d?\.?\d*\s*\)\s*$/,(.1*t).toString()+")"):0===e.indexOf("rgb")?n.rgbToRgba(e):0===e.indexOf("#")?n.hexToRgba(e):void 0},o.prototype.bindEvents=function(){this.$element.on("mousedown",t.bind(this,this.handleMousedown)),this.$element.on("mouseup touchend",t.bind(this,this.handleMouseup)),this.$element.on("mouseleave",t.bind(this,this.handleMouseup)),this.$element.on("touchmove",t.bind(this,this.handleTouchmove))},o.prototype.handleMousedown=function(e){if(!this.mousedown)if(e.hasOwnProperty("originalEvent")&&(e=e.originalEvent),this.mousedown=!0,this.options.center)this.createRipple(this.container.prop("clientWidth")/2,this.container.prop("clientWidth")/2);else if(e.srcElement!==this.$element[0]){var t=this.$element[0].getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top;this.createRipple(n,o)}else this.createRipple(e.offsetX,e.offsetY)},o.prototype.handleMouseup=function(){i(this,this.clearRipples)},o.prototype.handleTouchmove=function(){i(this,this.deleteRipples)},o.prototype.deleteRipples=function(){for(var e=0;e<this.ripples.length;e++)this.ripples[e].remove()},o.prototype.clearRipples=function(){for(var e=0;e<this.ripples.length;e++)this.fadeInComplete(this.ripples[e])},o.prototype.createContainer=function(){var e=t.element('<div class="md-ripple-container"></div>');return this.$element.append(e),e},o.prototype.clearTimeout=function(){this.timeout&&(this.$timeout.cancel(this.timeout),this.timeout=null)},o.prototype.isRippleAllowed=function(){var e=this.$element[0];do{if(!e.tagName||"BODY"===e.tagName)break;if(e&&t.isFunction(e.hasAttribute)){if(e.hasAttribute("disabled"))return!1;if("false"===this.inkRipple()||"0"===this.inkRipple())return!1}}while(e=e.parentNode);return!0},o.prototype.inkRipple=function(){return this.$element.attr("md-ink-ripple")},o.prototype.createRipple=function(e,n){function o(e,t,n){return e?Math.max(t,n):Math.sqrt(Math.pow(t,2)+Math.pow(n,2))}if(this.isRippleAllowed()){var i=this,r=i.$mdColorUtil,d=t.element('<div class="md-ripple"></div>'),s=this.$element.prop("clientWidth"),c=this.$element.prop("clientHeight"),l=2*Math.max(Math.abs(s-e),e),m=2*Math.max(Math.abs(c-n),n),u=o(this.options.fitRipple,l,m),h=this.calculateColor();d.css({left:e+"px",top:n+"px",background:"black",width:u+"px",height:u+"px",backgroundColor:r.rgbaToRgb(h),borderColor:r.rgbaToRgb(h)}),this.lastRipple=d,this.clearTimeout(),this.timeout=this.$timeout(function(){i.clearTimeout(),i.mousedown||i.fadeInComplete(d)},.35*a,!1),this.options.dimBackground&&this.container.css({backgroundColor:h}),this.container.append(d),this.ripples.push(d),d.addClass("md-ripple-placed"),this.$mdUtil.nextTick(function(){d.addClass("md-ripple-scaled md-ripple-active"),i.$timeout(function(){i.clearRipples()},a,!1)},!1)}},o.prototype.fadeInComplete=function(e){this.lastRipple===e?this.timeout||this.mousedown||this.removeRipple(e):this.removeRipple(e)},o.prototype.removeRipple=function(e){var t=this,n=this.ripples.indexOf(e);0>n||(this.ripples.splice(this.ripples.indexOf(e),1),e.removeClass("md-ripple-active"),0===this.ripples.length&&this.container.css({backgroundColor:""}),this.$timeout(function(){t.fadeOutComplete(e)},a,!1))},o.prototype.fadeOutComplete=function(e){e.remove(),this.lastRipple=null}}(),function(){!function(){function e(e){function n(n,o,i){return e.attach(n,o,t.extend({center:!1,dimBackground:!0,outline:!1,rippleSize:"full"},i))}return{attach:n}}t.module("material.core").factory("$mdTabInkRipple",e),e.$inject=["$mdInkRipple"]}()}(),function(){t.module("material.core.theming.palette",[]).constant("$mdColorPalette",{red:{50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 A100",contrastStrongLightColors:"400 500 600 700 A200 A400 A700"},pink:{50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"500 600 A200 A400 A700"},purple:{50:"#f3e5f5",100:"#e1bee7",200:"#ce93d8",300:"#ba68c8",400:"#ab47bc",500:"#9c27b0",600:"#8e24aa",700:"#7b1fa2",800:"#6a1b9a",900:"#4a148c",A100:"#ea80fc",A200:"#e040fb",A400:"#d500f9",A700:"#aa00ff",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200 A400 A700"},"deep-purple":{50:"#ede7f6",100:"#d1c4e9",200:"#b39ddb",300:"#9575cd",400:"#7e57c2",500:"#673ab7",600:"#5e35b1",700:"#512da8",800:"#4527a0",900:"#311b92",A100:"#b388ff",A200:"#7c4dff",A400:"#651fff",A700:"#6200ea",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200"},indigo:{50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100",contrastStrongLightColors:"300 400 A200 A400"},blue:{50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 400 A100",contrastStrongLightColors:"500 600 700 A200 A400 A700"},"light-blue":{50:"#e1f5fe",100:"#b3e5fc",200:"#81d4fa",300:"#4fc3f7",400:"#29b6f6",500:"#03a9f4",600:"#039be5",700:"#0288d1",800:"#0277bd",900:"#01579b",A100:"#80d8ff",A200:"#40c4ff",A400:"#00b0ff",A700:"#0091ea",contrastDefaultColor:"dark",contrastLightColors:"600 700 800 900 A700",contrastStrongLightColors:"600 700 800 A700"},cyan:{50:"#e0f7fa",100:"#b2ebf2",200:"#80deea",300:"#4dd0e1",400:"#26c6da",500:"#00bcd4",600:"#00acc1",700:"#0097a7",800:"#00838f",900:"#006064",A100:"#84ffff",A200:"#18ffff",A400:"#00e5ff",A700:"#00b8d4",contrastDefaultColor:"dark",contrastLightColors:"700 800 900",contrastStrongLightColors:"700 800 900"},teal:{50:"#e0f2f1",100:"#b2dfdb",200:"#80cbc4",300:"#4db6ac",400:"#26a69a",500:"#009688",600:"#00897b",700:"#00796b",800:"#00695c",900:"#004d40",A100:"#a7ffeb",A200:"#64ffda",A400:"#1de9b6",A700:"#00bfa5",contrastDefaultColor:"dark",contrastLightColors:"500 600 700 800 900",contrastStrongLightColors:"500 600 700"},green:{50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853",contrastDefaultColor:"dark",contrastLightColors:"500 600 700 800 900",contrastStrongLightColors:"500 600 700"},"light-green":{50:"#f1f8e9",100:"#dcedc8",200:"#c5e1a5",300:"#aed581",400:"#9ccc65",500:"#8bc34a",600:"#7cb342",700:"#689f38",800:"#558b2f",900:"#33691e",A100:"#ccff90",A200:"#b2ff59",A400:"#76ff03",A700:"#64dd17",contrastDefaultColor:"dark",contrastLightColors:"700 800 900",contrastStrongLightColors:"700 800 900"},lime:{50:"#f9fbe7",100:"#f0f4c3",200:"#e6ee9c",300:"#dce775",400:"#d4e157",500:"#cddc39",600:"#c0ca33",700:"#afb42b",800:"#9e9d24",900:"#827717",A100:"#f4ff81",A200:"#eeff41",A400:"#c6ff00",A700:"#aeea00",contrastDefaultColor:"dark",contrastLightColors:"900",contrastStrongLightColors:"900"},yellow:{50:"#fffde7",100:"#fff9c4",200:"#fff59d",300:"#fff176",400:"#ffee58",500:"#ffeb3b",600:"#fdd835",700:"#fbc02d",800:"#f9a825",900:"#f57f17",A100:"#ffff8d",A200:"#ffff00",A400:"#ffea00",A700:"#ffd600",contrastDefaultColor:"dark"},amber:{50:"#fff8e1",100:"#ffecb3",200:"#ffe082",300:"#ffd54f",400:"#ffca28",500:"#ffc107",600:"#ffb300",700:"#ffa000",800:"#ff8f00",900:"#ff6f00",A100:"#ffe57f",A200:"#ffd740",A400:"#ffc400",A700:"#ffab00",contrastDefaultColor:"dark"},orange:{50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00",contrastDefaultColor:"dark",contrastLightColors:"800 900",contrastStrongLightColors:"800 900"},"deep-orange":{50:"#fbe9e7",100:"#ffccbc",200:"#ffab91",300:"#ff8a65",400:"#ff7043",500:"#ff5722",600:"#f4511e",700:"#e64a19",800:"#d84315",900:"#bf360c",A100:"#ff9e80",A200:"#ff6e40",A400:"#ff3d00",A700:"#dd2c00",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 400 A100 A200",contrastStrongLightColors:"500 600 700 800 900 A400 A700"},brown:{50:"#efebe9",100:"#d7ccc8",200:"#bcaaa4",300:"#a1887f",400:"#8d6e63",500:"#795548",600:"#6d4c41",700:"#5d4037",800:"#4e342e",900:"#3e2723",A100:"#d7ccc8",A200:"#bcaaa4",A400:"#8d6e63",A700:"#5d4037",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 A100 A200",contrastStrongLightColors:"300 400"},grey:{50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#ffffff",A200:"#000000",A400:"#303030",A700:"#616161",contrastDefaultColor:"dark",contrastLightColors:"600 700 800 900 A200 A400 A700"},"blue-grey":{50:"#eceff1",100:"#cfd8dc",200:"#b0bec5",300:"#90a4ae",400:"#78909c",500:"#607d8b",600:"#546e7a",700:"#455a64",800:"#37474f",900:"#263238",A100:"#cfd8dc",A200:"#b0bec5",A400:"#78909c",A700:"#455a64",contrastDefaultColor:"light",contrastDarkColors:"50 100 200 300 A100 A200",contrastStrongLightColors:"400 500 700"}})}(),function(){function e(e){function o(e,t){return t=t||{},m[e]=a(e,t),b}function i(e,n){return a(e,t.extend({},m[e]||{},n))}function a(e,t){var n=_.filter(function(e){return!t[e]});if(n.length)throw new Error("Missing colors %1 in palette %2!".replace("%1",n.join(", ")).replace("%2",e));return t}function s(e,n){if(v[e])return v[e];n=n||"default";var o="string"==typeof n?v[n]:n,i=new c(e);return o&&t.forEach(o.colors,function(e,n){i.colors[n]={name:e.name,hues:t.extend({},e.hues)}}),v[e]=i,i}function c(e){function n(e){if(e=0===arguments.length?!0:!!e,e!==o.isDark){o.isDark=e,o.foregroundPalette=o.isDark?p:h,o.foregroundShadow=o.isDark?f:g;var n=o.isDark?M:y,i=o.isDark?y:M;return t.forEach(n,function(e,t){var n=o.colors[t],r=i[t];if(n)for(var a in n.hues)n.hues[a]===r[a]&&(n.hues[a]=e[a])}),o}}var o=this;o.name=e,o.colors={},o.dark=n,n(!1),$.forEach(function(e){var n=(o.isDark?M:y)[e];o[e+"Palette"]=function(i,r){var a=o.colors[e]={name:i,hues:t.extend({},n,r)};return Object.keys(a.hues).forEach(function(e){if(!n[e])throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4".replace("%1",e).replace("%2",o.name).replace("%3",i).replace("%4",Object.keys(n).join(", ")))}),Object.keys(a.hues).map(function(e){return a.hues[e]}).forEach(function(t){if(-1==_.indexOf(t))throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5".replace("%1",t).replace("%2",o.name).replace("%3",e).replace("%4",i).replace("%5",_.join(", ")))}),o},o[e+"Color"]=function(){var t=Array.prototype.slice.call(arguments);return console.warn("$mdThemingProviderTheme."+e+"Color() has been deprecated. Use $mdThemingProviderTheme."+e+"Palette() instead."),o[e+"Palette"].apply(o,t)}})}function u(e,o){function i(e){return e===n||""===e?!0:a.THEMES[e]!==n}function r(n,r){function a(){return s=r.controller("mdTheme")||n.data("$mdThemeController"),s&&s.$mdTheme||("default"==E?"":E)}function d(e){if(e){i(e)||o.warn("Attempted to use unregistered theme '"+e+"'. Register it with $mdThemingProvider.theme().");var t=n.data("$mdThemeName");t&&n.removeClass("md-"+t+"-theme"),n.addClass("md-"+e+"-theme"),n.data("$mdThemeName",e),s&&n.data("$mdThemeController",s)}}var s=r.controller("mdTheme"),c=n.attr("md-theme-watch"),l=(C||t.isDefined(c))&&"false"!=c;d(a()),n.on("$destroy",l?e.$watch(a,d):t.noop)}var a=function(t,o){o===n&&(o=t,t=n),t===n&&(t=e),a.inherit(o,o)};return a.THEMES=t.extend({},v),a.PALETTES=t.extend({},m),a.inherit=r,a.registered=i,a.defaultTheme=function(){return E},a.generateTheme=function(e){d(v[e],e,T)},a}m={};var b,v={},E="default",C=!1;return t.extend(m,e),u.$inject=["$rootScope","$log"],b={definePalette:o,extendPalette:i,theme:s,disableTheming:function(){w=!0},setNonce:function(e){T=e},setDefaultTheme:function(e){E=e},alwaysWatchTheme:function(e){C=e},generateThemesOnDemand:function(e){A=e},$get:u,_LIGHT_DEFAULT_HUES:y,_DARK_DEFAULT_HUES:M,_PALETTES:m,_THEMES:v,_parseRules:r,_rgba:l}}function o(e,n,o){return{priority:100,link:{pre:function(i,r,a){var d=[],s={registerChanges:function(e,n){return n&&(e=t.bind(n,e)),d.push(e),function(){var t=d.indexOf(e);t>-1&&d.splice(t,1)}},$setTheme:function(t){e.registered(t)||o.warn("attempted to use unregistered theme '"+t+"'"),s.$mdTheme=t,d.forEach(function(e){e()})}};r.data("$mdThemeController",s),s.$setTheme(n(a.mdTheme)(i)),a.$observe("mdTheme",s.$setTheme)}}}}function i(e){return e}function r(e,n,o){s(e,n),o=o.replace(/THEME_NAME/g,e.name);var i=[],r=e.colors[n],a=new RegExp(".md-"+e.name+"-theme","g"),d=new RegExp("('|\")?{{\\s*("+n+")-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|')?","g"),c=/'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g,u=m[r.name];return o=o.replace(c,function(t,n,o,i,r){return"foreground"===n?"shadow"==o?e.foregroundShadow:e.foregroundPalette[o]||e.foregroundPalette[1]:(0!==o.indexOf("hue")&&"default"!==o||(o=e.colors[n].hues[o]),l((m[e.colors[n].name][o]||"")[r?"contrast":"value"],i))}),t.forEach(r.hues,function(t,n){var r=o.replace(d,function(e,n,o,i,r){return l(u[t]["color"===i?"value":"contrast"],r)});if("default"!==n&&(r=r.replace(a,".md-"+e.name+"-theme.md-"+n)),"default"==e.name){var s=/((?:(?:(?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)+) )?)((?:(?:\w|\.|-)+)?)\.md-default-theme((?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)*)/g;r=r.replace(s,function(e,t,n,o){return e+", "+t+n+o})}i.push(r)}),i}function a(e,n){function o(e,n){var o=e.contrastDefaultColor,i=e.contrastLightColors||[],r=e.contrastStrongLightColors||[],a=e.contrastDarkColors||[];"string"==typeof i&&(i=i.split(" ")),"string"==typeof r&&(r=r.split(" ")),"string"==typeof a&&(a=a.split(" ")),delete e.contrastDefaultColor,delete e.contrastLightColors,delete e.contrastStrongLightColors,delete e.contrastDarkColors,t.forEach(e,function(n,d){function s(){return"light"===o?a.indexOf(d)>-1?b:r.indexOf(d)>-1?E:v:i.indexOf(d)>-1?r.indexOf(d)>-1?E:v:b}if(!t.isObject(n)){var l=c(n);if(!l)throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected.".replace("%1",n).replace("%2",e.name).replace("%3",d));e[d]={value:l,contrast:s()}}})}var i=document.head,r=i?i.firstElementChild:null,a=!w&&e.has("$MD_THEME_CSS")?e.get("$MD_THEME_CSS"):"";if(r&&0!==a.length){t.forEach(m,o);var s=a.split(/\}(?!(\}|'|"|;))/).filter(function(e){return e&&e.length}).map(function(e){return e.trim()+"}"}),l=new RegExp("md-("+$.join("|")+")","g");$.forEach(function(e){k[e]=""}),s.forEach(function(e){for(var t,n=(e.match(l),0);t=$[n];n++)if(e.indexOf(".md-"+t)>-1)return k[t]+=e;for(n=0;t=$[n];n++)if(e.indexOf(t)>-1)return k[t]+=e;return k[C]+=e}),A||t.forEach(n.THEMES,function(e){u[e.name]||"default"!==n.defaultTheme()&&"default"===e.name||d(e,e.name,T)})}}function d(e,t,n){var o=document.head,i=o?o.firstElementChild:null;u[t]||($.forEach(function(t){for(var a=r(e,t,k[t]);a.length;){var d=a.shift();if(d){var s=document.createElement("style");s.setAttribute("md-theme-style",""),n&&s.setAttribute("nonce",n),s.appendChild(document.createTextNode(d)),o.insertBefore(s,i)}}}),u[e.name]=!0)}function s(e,t){if(!m[(e.colors[t]||{}).name])throw new Error("You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3".replace("%1",e.name).replace("%2",t).replace("%3",Object.keys(m).join(", ")))}function c(e){if(t.isArray(e)&&3==e.length)return e;if(/^rgb/.test(e))return e.replace(/(^\s*rgba?\(|\)\s*$)/g,"").split(",").map(function(e,t){return 3==t?parseFloat(e,10):parseInt(e,10)});if("#"==e.charAt(0)&&(e=e.substring(1)),/^([a-fA-F0-9]{3}){1,2}$/g.test(e)){var n=e.length/3,o=e.substr(0,n),i=e.substr(n,n),r=e.substr(2*n);return 1===n&&(o+=o,i+=i,r+=r),[parseInt(o,16),parseInt(i,16),parseInt(r,16)]}}function l(e,n){return e?(4==e.length&&(e=t.copy(e),n?e.pop():n=e.pop()),n&&("number"==typeof n||"string"==typeof n&&n.length)?"rgba("+e.join(",")+","+n+")":"rgb("+e.join(",")+")"):"rgb('0,0,0')"}t.module("material.core.theming",["material.core.theming.palette"]).directive("mdTheme",o).directive("mdThemable",i).provider("$mdTheming",e).run(a);var m,u={},h={name:"dark",1:"rgba(0,0,0,0.87)",2:"rgba(0,0,0,0.54)",3:"rgba(0,0,0,0.38)",4:"rgba(0,0,0,0.12)"},p={name:"light",1:"rgba(255,255,255,1.0)",2:"rgba(255,255,255,0.7)",3:"rgba(255,255,255,0.5)",4:"rgba(255,255,255,0.12)"},f="1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)",g="",b=c("rgba(0,0,0,0.87)"),v=c("rgba(255,255,255,0.87)"),E=c("rgb(255,255,255)"),$=["primary","accent","warn","background"],C="primary",y={accent:{"default":"A200","hue-1":"A100","hue-2":"A400","hue-3":"A700"},background:{"default":"50","hue-1":"A100","hue-2":"100","hue-3":"300"}},M={background:{"default":"A400","hue-1":"800","hue-2":"900","hue-3":"A200"}};$.forEach(function(e){var t={"default":"500","hue-1":"300","hue-2":"800","hue-3":"A100"};y[e]||(y[e]=t),M[e]||(M[e]=t)});var _=["50","100","200","300","400","500","600","700","800","900","A100","A200","A400","A700"],A=!1,T=null,w=!1;e.$inject=["$mdColorPalette"],o.$inject=["$mdTheming","$interpolate","$log"],i.$inject=["$mdTheming"];var k={};a.$inject=["$injector","$mdTheming"]}(),function(){function n(n,o,i,r,a){var d;return d={translate3d:function(e,t,n,o){function i(n){return a(e,{to:n||t,addClass:o.transitionOutClass,removeClass:o.transitionInClass}).start()}return a(e,{from:t,to:n,addClass:o.transitionInClass,removeClass:o.transitionOutClass}).start().then(function(){return i})},waitTransitionEnd:function(t,n){var a=3e3;return o(function(o,d){function s(e){e&&e.target!==t[0]||(e&&i.cancel(l),t.off(r.CSS.TRANSITIONEND,s),o())}function c(n){return n=n||e.getComputedStyle(t[0]),"0s"==n.transitionDuration||!n.transition&&!n.transitionProperty}n=n||{},c(n.cachedTransitionStyles)&&(a=0);var l=i(s,n.timeout||a);t.on(r.CSS.TRANSITIONEND,s)})},calculateTransformValues:function(e,t){function n(){var t=e?e.parent():null,n=t?t.parent():null;return n?d.clientRect(n):null}var o=t.element,i=t.bounds;if(o||i){var r=o?d.clientRect(o)||n():d.copyRect(i),a=d.copyRect(e[0].getBoundingClientRect()),s=d.centerPointFor(a),c=d.centerPointFor(r);return{centerX:c.x-s.x,centerY:c.y-s.y,scaleX:Math.round(100*Math.min(.5,r.width/a.width))/100,scaleY:Math.round(100*Math.min(.5,r.height/a.height))/100}}return{centerX:0,centerY:0,scaleX:.5,scaleY:.5}},calculateZoomToOrigin:function(e,o){var i="translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )",r=t.bind(null,n.supplant,i);return r(d.calculateTransformValues(e,o))},calculateSlideToOrigin:function(e,o){var i="translate3d( {centerX}px, {centerY}px, 0 )",r=t.bind(null,n.supplant,i);return r(d.calculateTransformValues(e,o))},toCss:function(e){function n(e,n,i){t.forEach(n.split(" "),function(e){o[e]=i})}var o={},i="left top right bottom width height x y min-width min-height max-width max-height";return t.forEach(e,function(e,a){if(!t.isUndefined(e))if(i.indexOf(a)>=0)o[a]=e+"px";else switch(a){case"transition":n(a,r.CSS.TRANSITION,e);break;case"transform":n(a,r.CSS.TRANSFORM,e);break;case"transformOrigin":n(a,r.CSS.TRANSFORM_ORIGIN,e)}}),o},toTransformCss:function(e,n,o){var i={};return t.forEach(r.CSS.TRANSFORM.split(" "),function(t){i[t]=e}),n&&(o=o||"all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important",i.transition=o),i},copyRect:function(e,n){return e?(n=n||{},t.forEach("left top right bottom width height".split(" "),function(t){n[t]=Math.round(e[t])}),n.width=n.width||n.right-n.left,n.height=n.height||n.bottom-n.top,n):null},clientRect:function(e){var n=t.element(e)[0].getBoundingClientRect(),o=function(e){return e&&e.width>0&&e.height>0};return o(n)?d.copyRect(n):null},centerPointFor:function(e){return e?{x:Math.round(e.left+e.width/2),y:Math.round(e.top+e.height/2)}:{x:0,y:0}}}}t.module("material.core").factory("$$mdAnimate",["$q","$timeout","$mdConstant","$animateCss",function(e,t,o,i){return function(r){return n(r,e,t,o,i)}}])}(),function(){t.version.minor>=4?t.module("material.core.animate",[]):!function(){function e(e){return e.replace(/-[a-z]/g,function(e){return e.charAt(1).toUpperCase()})}var n=t.forEach,o=t.isDefined(document.documentElement.style.WebkitAppearance),i=o?"-webkit-":"",r=(o?"webkitTransitionEnd ":"")+"transitionend",a=(o?"webkitAnimationEnd ":"")+"animationend",d=["$document",function(e){return function(){return e[0].body.clientWidth+1}}],s=["$$rAF",function(e){return function(){var t=!1;return e(function(){t=!0}),function(n){t?n():e(n)}}}],c=["$q","$$rAFMutex",function(e,o){function i(e){this.setHost(e),this._doneCallbacks=[],this._runInAnimationFrame=o(),this._state=0}var r=0,a=1,d=2;return i.prototype={setHost:function(e){this.host=e||{}},done:function(e){this._state===d?e():this._doneCallbacks.push(e)},progress:t.noop,getPromise:function(){if(!this.promise){var t=this;this.promise=e(function(e,n){t.done(function(t){t===!1?n():e()})})}return this.promise},then:function(e,t){return this.getPromise().then(e,t)},"catch":function(e){return this.getPromise()["catch"](e)},"finally":function(e){return this.getPromise()["finally"](e)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end(),this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel(),this._resolve(!1)},complete:function(e){var t=this;t._state===r&&(t._state=a,t._runInAnimationFrame(function(){t._resolve(e)}))},_resolve:function(e){this._state!==d&&(n(this._doneCallbacks,function(t){t(e)}),this._doneCallbacks.length=0,this._state=d)}},i}];t.module("material.core.animate",[]).factory("$$forceReflow",d).factory("$$AnimateRunner",c).factory("$$rAFMutex",s).factory("$animateCss",["$window","$$rAF","$$AnimateRunner","$$forceReflow","$$jqLite","$timeout","$animate",function(t,d,s,c,l,m,u){function h(o,d){var c=[],l=C(o),h=l&&u.enabled(),g=!1,M=!1;h&&(d.transitionStyle&&c.push([i+"transition",d.transitionStyle]),d.keyframeStyle&&c.push([i+"animation",d.keyframeStyle]),d.delay&&c.push([i+"transition-delay",d.delay+"s"]),d.duration&&c.push([i+"transition-duration",d.duration+"s"]),g=d.keyframeStyle||d.to&&(d.duration>0||d.transitionStyle),M=!!d.addClass||!!d.removeClass,y(o,!0));var _=h&&(g||M);E(o,d);var A,T,w=!1;return{close:t.close,start:function(){function t(){return w?void 0:(w=!0,A&&T&&o.off(A,T),p(o,d),v(o,d),n(c,function(t){l.style[e(t[0])]=""}),u.complete(!0),u)}var u=new s;return b(function(){if(y(o,!1),!_)return t();n(c,function(t){var n=t[0],o=t[1];l.style[e(n)]=o}),p(o,d);var s=f(o);if(0===s.duration)return t();var u=[];d.easing&&(s.transitionDuration&&u.push([i+"transition-timing-function",d.easing]),s.animationDuration&&u.push([i+"animation-timing-function",d.easing])),d.delay&&s.animationDelay&&u.push([i+"animation-delay",d.delay+"s"]),d.duration&&s.animationDuration&&u.push([i+"animation-duration",d.duration+"s"]),n(u,function(t){var n=t[0],o=t[1];l.style[e(n)]=o,c.push(t)});var h=s.delay,g=1e3*h,b=s.duration,v=1e3*b,E=Date.now();A=[],s.transitionDuration&&A.push(r),s.animationDuration&&A.push(a),A=A.join(" "),T=function(e){e.stopPropagation();var n=e.originalEvent||e,o=n.timeStamp||Date.now(),i=parseFloat(n.elapsedTime.toFixed(3));Math.max(o-E,0)>=g&&i>=b&&t()},o.on(A,T),$(o,d),m(t,g+1.5*v,!1)}),u}}}function p(e,t){t.addClass&&(l.addClass(e,t.addClass),t.addClass=null),t.removeClass&&(l.removeClass(e,t.removeClass),t.removeClass=null)}function f(e){function n(e){return o?"Webkit"+e.charAt(0).toUpperCase()+e.substr(1):e}var i=C(e),r=t.getComputedStyle(i),a=g(r[n("transitionDuration")]),d=g(r[n("animationDuration")]),s=g(r[n("transitionDelay")]),c=g(r[n("animationDelay")]);d*=parseInt(r[n("animationIterationCount")],10)||1;var l=Math.max(d,a),m=Math.max(c,s);return{duration:l,delay:m,animationDuration:d,transitionDuration:a,animationDelay:c,transitionDelay:s}}function g(e){var t=0,o=(e||"").split(/\s*,\s*/);return n(o,function(e){"s"==e.charAt(e.length-1)&&(e=e.substring(0,e.length-1)),e=parseFloat(e)||0,t=t?Math.max(e,t):e}),t}function b(e){M&&M(),_.push(e),M=d(function(){M=null;for(var e=c(),t=0;t<_.length;t++)_[t](e);_.length=0})}function v(e,t){E(e,t),$(e,t)}function E(e,t){t.from&&(e.css(t.from),t.from=null)}function $(e,t){t.to&&(e.css(t.to),t.to=null)}function C(e){for(var t=0;t<e.length;t++)if(1===e[t].nodeType)return e[t]}function y(t,n){var o=C(t),r=e(i+"transition-delay");o.style[r]=n?"-9999s":""}var M,_=[];return h}])}()}(),function(){t.module("material.components.autocomplete",["material.core","material.components.icon","material.components.virtualRepeat"])}(),function(){t.module("material.components.backdrop",["material.core"]).directive("mdBackdrop",["$mdTheming","$mdUtil","$animate","$rootElement","$window","$log","$$rAF","$document",function(e,t,n,o,i,r,a,d){function s(t,s,l){n.pin&&n.pin(s,o),a(function(){var t=i.getComputedStyle(d[0].body);if("fixed"==t.position){var n=parseInt(t.height,10)+Math.abs(parseInt(t.top,10));s.css({height:n+"px"})}var o=s.parent()[0];if(o){"BODY"==o.nodeName&&s.css({position:"fixed"});var a=i.getComputedStyle(o);"static"==a.position&&r.warn(c)}s.parent().length&&e.inherit(s,s.parent())})}var c="<md-backdrop> may not work properly in a scrolled, static-positioned parent container.";return{restrict:"E",link:s}}])}(),function(){function e(e){return{restrict:"E",link:function(t,n){e(n)}}}function n(e,n,o,i){function r(e){return t.isDefined(e.href)||t.isDefined(e.ngHref)||t.isDefined(e.ngLink)||t.isDefined(e.uiSref)}function a(e,t){if(r(t))return'<a class="md-button" ng-transclude></a>';var n="undefined"==typeof t.type?"button":t.type;return'<button class="md-button" type="'+n+'" ng-transclude></button>'}function d(a,d,s){n(d),e.attach(a,d),o.expectWithText(d,"aria-label"),r(s)&&t.isDefined(s.ngDisabled)&&a.$watch(s.ngDisabled,function(e){d.attr("tabindex",e?-1:0)}),d.on("click",function(e){s.disabled===!0&&(e.preventDefault(),e.stopImmediatePropagation())}),t.isDefined(s.mdNoFocusStyle)||(a.mouseActive=!1,d.on("mousedown",function(){a.mouseActive=!0,i(function(){a.mouseActive=!1},100)}).on("focus",function(){a.mouseActive===!1&&d.addClass("md-focused")}).on("blur",function(e){d.removeClass("md-focused")}))}return{restrict:"EA",replace:!0,transclude:!0,template:a,link:d}}t.module("material.components.button",["material.core"]).directive("mdButton",n).directive("a",e),e.$inject=["$mdTheming"],n.$inject=["$mdButtonInkRipple","$mdTheming","$mdAria","$timeout"]; +}(),function(){function e(e){return{restrict:"E",link:function(t,n){n.addClass("_md"),t.$on("$destroy",function(){e.destroy()})}}}function n(e){function n(e,n,r,a,d,s,c){function l(o,i,c,l){i=r.extractElementByName(i,"md-bottom-sheet"),i.attr("tabindex","-1"),c.disableBackdrop||(h=r.createBackdrop(o,"_md-bottom-sheet-backdrop md-opaque"),h[0].tabIndex=-1,c.clickOutsideToClose&&h.on("click",function(){r.nextTick(d.cancel,!0)}),a.inherit(h,c.parent),e.enter(h,c.parent,null));var m=new u(i,c.parent);return c.bottomSheet=m,a.inherit(m.element,c.parent),c.disableParentScroll&&(c.restoreScroll=r.disableScrollAround(m.element,c.parent)),e.enter(m.element,c.parent,h).then(function(){var e=r.findFocusTarget(i)||t.element(i[0].querySelector("button")||i[0].querySelector("a")||i[0].querySelector(r.prefixer("ng-click",!0)))||h;c.escapeToClose&&(c.rootElementKeyupCallback=function(e){e.keyCode===n.KEY_CODE.ESCAPE&&r.nextTick(d.cancel,!0)},s.on("keyup",c.rootElementKeyupCallback),e&&e.focus())})}function m(t,n,o){var i=o.bottomSheet;return o.disableBackdrop||e.leave(h),e.leave(i.element).then(function(){o.disableParentScroll&&(o.restoreScroll(),delete o.restoreScroll),i.cleanup()})}function u(e,t){function a(t){e.css(n.CSS.TRANSITION_DURATION,"0ms")}function s(t){var o=t.pointer.distanceY;5>o&&(o=Math.max(-i,o/2)),e.css(n.CSS.TRANSFORM,"translate3d(0,"+(i+o)+"px,0)")}function l(t){if(t.pointer.distanceY>0&&(t.pointer.distanceY>20||Math.abs(t.pointer.velocityY)>o)){var i=e.prop("offsetHeight")-t.pointer.distanceY,a=Math.min(i/t.pointer.velocityY*.75,500);e.css(n.CSS.TRANSITION_DURATION,a+"ms"),r.nextTick(d.cancel,!0)}else e.css(n.CSS.TRANSITION_DURATION,""),e.css(n.CSS.TRANSFORM,"")}var m=c.register(t,"drag",{horizontal:!1});return t.on("$md.dragstart",a).on("$md.drag",s).on("$md.dragend",l),{element:e,cleanup:function(){m(),t.off("$md.dragstart",a),t.off("$md.drag",s),t.off("$md.dragend",l)}}}var h;return{themable:!0,onShow:l,onRemove:m,disableBackdrop:!1,escapeToClose:!0,clickOutsideToClose:!0,disableParentScroll:!0}}var o=.5,i=80;return n.$inject=["$animate","$mdConstant","$mdUtil","$mdTheming","$mdBottomSheet","$rootElement","$mdGesture"],e("$mdBottomSheet").setDefaults({methods:["disableParentScroll","escapeToClose","clickOutsideToClose"],options:n})}t.module("material.components.bottomSheet",["material.core","material.components.backdrop"]).directive("mdBottomSheet",e).provider("$mdBottomSheet",n),e.$inject=["$mdBottomSheet"],n.$inject=["$$interimElementProvider"]}(),function(){function e(e,n,o,i,r,a){function d(d,c){var l=d.children(),m=r.parseAttributeBoolean(c.mdIndeterminate);return c.$set("tabindex",c.tabindex||"0"),c.$set("type","checkbox"),c.$set("role",c.type),d.on("click",function(e){this.hasAttribute("disabled")&&e.stopImmediatePropagation()}),l.on("focus",function(){d.focus()}),function(d,c,l,u){function h(e,t,n){l[e]&&d.$watch(l[e],function(e){n[e]&&c.attr(t,n[e])})}function p(e){var t=e.which||e.keyCode;t!==o.KEY_CODE.SPACE&&t!==o.KEY_CODE.ENTER||(e.preventDefault(),c.hasClass("md-focused")||c.addClass("md-focused"),f(e))}function f(e){c[0].hasAttribute("disabled")||d.$apply(function(){var t=l.ngChecked?l.checked:!u.$viewValue;u.$setViewValue(t,e&&e.type),u.$render()})}function g(){u.$viewValue&&!v?c.addClass(s):c.removeClass(s)}function b(e){v=e!==!1,v&&c.attr("aria-checked","mixed"),c.toggleClass("md-indeterminate",v)}var v;u=u||r.fakeNgModel(),i(c),m&&(b(),d.$watch(l.mdIndeterminate,b)),l.ngChecked&&d.$watch(d.$eval.bind(d,l.ngChecked),u.$setViewValue.bind(u)),h("ngDisabled","tabindex",{"true":"-1","false":l.tabindex}),n.expectWithText(c,"aria-label"),e.link.pre(d,{on:t.noop,0:{}},l,[u]),d.mouseActive=!1,c.on("click",f).on("keypress",p).on("mousedown",function(){d.mouseActive=!0,a(function(){d.mouseActive=!1},100)}).on("focus",function(){d.mouseActive===!1&&c.addClass("md-focused")}).on("blur",function(){c.removeClass("md-focused")}),u.$render=g}}e=e[0];var s="md-checked";return{restrict:"E",transclude:!0,require:"?ngModel",priority:210,template:'<div class="_md-container" md-ink-ripple md-ink-ripple-checkbox><div class="_md-icon"></div></div><div ng-transclude class="_md-label"></div>',compile:d}}t.module("material.components.checkbox",["material.core"]).directive("mdCheckbox",e),e.$inject=["inputDirective","$mdAria","$mdConstant","$mdTheming","$mdUtil","$timeout"]}(),function(){t.module("material.components.chips",["material.core","material.components.autocomplete"])}(),function(){function e(e){return{restrict:"E",link:function(t,n,o){n.addClass("_md"),e(n)}}}t.module("material.components.card",["material.core"]).directive("mdCard",e),e.$inject=["$mdTheming"]}(),function(){!function(){function e(e,n,o){function i(e,t){try{e.css(s(t))}catch(n){o.error(n.message)}}function a(e){var t=l(e);return d(t)}function d(t,o){o=o||!1;var i=e.PALETTES[t.palette][t.hue];return i=o?i.contrast:i.value,n.supplant("rgba( {0}, {1}, {2}, {3} )",[i[0],i[1],i[2],i[3]||t.opacity])}function s(e){var n={},o=e.hasOwnProperty("color");return t.forEach(e,function(e,t){var i=l(e),r=t.indexOf("background")>-1;n[t]=d(i),r&&!o&&(n.color=d(i,!0))}),n}function c(n){return t.isDefined(e.THEMES[n.split("-")[0]])}function l(n){var o=n.split("-"),i=t.isDefined(e.THEMES[o[0]]),r=i?o.splice(0,1)[0]:e.defaultTheme();return{theme:r,palette:m(o,r),hue:u(o,r),opacity:o[2]||1}}function m(t,o){var i=t.length>1&&-1!==r.indexOf(t[1]),a=t[0].replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();if(i&&(a=t[0]+"-"+t.splice(1,1)),-1===r.indexOf(a)){var d=e.THEMES[o].colors[a];if(!d)throw new Error(n.supplant("mdColors: couldn't find '{palette}' in the palettes.",{palette:a}));a=d.name}return a}function u(t,o){var i=e.THEMES[o].colors;if("hue"===t[1]){var r=parseInt(t.splice(2,1)[0],10);if(1>r||r>3)throw new Error(n.supplant("mdColors: 'hue-{hueNumber}' is not a valid hue, can be only 'hue-1', 'hue-2' and 'hue-3'",{hueNumber:r}));if(t[1]="hue-"+r,!(t[0]in i))throw new Error(n.supplant("mdColors: 'hue-x' can only be used with [{availableThemes}], but was used with '{usedTheme}'",{availableThemes:Object.keys(i).join(", "),usedTheme:t[0]}));return i[t[0]].hues[t[1]]}return t[1]||i[t[0]in i?t[0]:"primary"].hues["default"]}return r=r||Object.keys(e.PALETTES),{applyThemeColors:i,getThemeColor:a,hasTheme:c}}function o(e,n,o,r){return{restrict:"A",require:["^?mdTheme"],compile:function(a,d){function s(){var e=d.mdColors,o=e.indexOf("::")>-1,r=o?!0:i.test(d.mdColors);d.mdColors=e.replace("::","");var a=t.isDefined(d.mdColorsWatch);return o||r?!1:a?n.parseAttributeBoolean(d.mdColorsWatch):!0}var c=s();return function(n,i,a,d){var s=d[0],l=function(t){var o=r(a.mdColors)(n);return s&&Object.keys(o).forEach(function(n){var i=o[n];e.hasTheme(i)||(o[n]=(t||s.$mdTheme)+"-"+i)}),o},m=t.noop;s&&(m=s.registerChanges(function(t){e.applyThemeColors(i,l(t))})),n.$on("destroy",function(){m()});try{c?n.$watch(l,t.bind(this,e.applyThemeColors,i),!0):e.applyThemeColors(i,l())}catch(u){o.error(u.message)}}}}}var i=/^{((\s|,)*?["'a-zA-Z-]+?\s*?:\s*?('|")[a-zA-Z0-9-.]*('|"))+\s*}$/,r=n;t.module("material.components.colors",["material.core"]).directive("mdColors",o).service("$mdColors",e),e.$inject=["$mdTheming","$mdUtil","$log"],o.$inject=["$mdColors","$mdUtil","$log","$parse"]}()}(),function(){function e(e){function t(e,t){this.$scope=e,this.$element=t}return{restrict:"E",controller:["$scope","$element",t],link:function(t,o){o.addClass("_md"),e(o),t.$broadcast("$mdContentLoaded",o),n(o[0])}}}function n(e){t.element(e).on("$md.pressdown",function(t){"t"===t.pointer.type&&(t.$materialScrollFixed||(t.$materialScrollFixed=!0,0===e.scrollTop?e.scrollTop=1:e.scrollHeight===e.scrollTop+e.offsetHeight&&(e.scrollTop-=1)))})}t.module("material.components.content",["material.core"]).directive("mdContent",e),e.$inject=["$mdTheming"]}(),function(){!function(){function e(){return{template:function(e,t){var n=t.hasOwnProperty("ngIf")?"":'ng-if="calendarCtrl.isInitialized"',o='<div ng-switch="calendarCtrl.currentView" '+n+'><md-calendar-year ng-switch-when="year"></md-calendar-year><md-calendar-month ng-switch-default></md-calendar-month></div>';return o},scope:{minDate:"=mdMinDate",maxDate:"=mdMaxDate",dateFilter:"=mdDateFilter"},require:["ngModel","mdCalendar"],controller:n,controllerAs:"calendarCtrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1];r.configureNgModel(i)}}}function n(e,n,o,r,a,d,s,c){d(e),this.$element=e,this.$scope=n,this.dateUtil=o,this.$mdUtil=r,this.keyCode=a.KEY_CODE,this.$$rAF=s,this.today=this.dateUtil.createDateAtMidnight(),this.ngModelCtrl=null,this.currentView="month",this.SELECTED_DATE_CLASS="md-calendar-selected-date",this.TODAY_CLASS="md-calendar-date-today",this.FOCUSED_DATE_CLASS="md-focus",this.id=i++,this.displayDate=null,this.selectedDate=null,this.isInitialized=!1,this.width=0,this.scrollbarWidth=0,c.tabindex||e.attr("tabindex","-1"),e.on("keydown",t.bind(this,this.handleKeyEvent))}t.module("material.components.datepicker",["material.core","material.components.icon","material.components.virtualRepeat"]).directive("mdCalendar",e);var o=340,i=0;n.$inject=["$element","$scope","$$mdDateUtil","$mdUtil","$mdConstant","$mdTheming","$$rAF","$attrs"],n.prototype.configureNgModel=function(e){var t=this;t.ngModelCtrl=e,t.$mdUtil.nextTick(function(){t.isInitialized=!0}),e.$render=function(){var e=this.$viewValue;t.$scope.$broadcast("md-calendar-parent-changed",e),t.selectedDate||(t.selectedDate=e),t.displayDate||(t.displayDate=t.selectedDate||t.today)}},n.prototype.setNgModelValue=function(e){var t=this.dateUtil.createDateAtMidnight(e);return this.focus(t),this.$scope.$emit("md-calendar-change",t),this.ngModelCtrl.$setViewValue(t),this.ngModelCtrl.$render(),t},n.prototype.setCurrentView=function(e,n){var o=this;o.$mdUtil.nextTick(function(){o.currentView=e,n&&(o.displayDate=t.isDate(n)?n:new Date(n))})},n.prototype.focus=function(e){if(this.dateUtil.isValidDate(e)){var t=this.$element[0].querySelector(".md-focus");t&&t.classList.remove(this.FOCUSED_DATE_CLASS);var n=this.getDateId(e,this.currentView),o=document.getElementById(n);o&&(o.classList.add(this.FOCUSED_DATE_CLASS),o.focus(),this.displayDate=e)}else{var i=this.$element[0].querySelector("[ng-switch]");i&&i.focus()}},n.prototype.getActionFromKeyEvent=function(e){var t=this.keyCode;switch(e.which){case t.ENTER:return"select";case t.RIGHT_ARROW:return"move-right";case t.LEFT_ARROW:return"move-left";case t.DOWN_ARROW:return e.metaKey?"move-page-down":"move-row-down";case t.UP_ARROW:return e.metaKey?"move-page-up":"move-row-up";case t.PAGE_DOWN:return"move-page-down";case t.PAGE_UP:return"move-page-up";case t.HOME:return"start";case t.END:return"end";default:return null}},n.prototype.handleKeyEvent=function(e){var t=this;this.$scope.$apply(function(){if(e.which==t.keyCode.ESCAPE||e.which==t.keyCode.TAB)return t.$scope.$emit("md-calendar-close"),void(e.which==t.keyCode.TAB&&e.preventDefault());var n=t.getActionFromKeyEvent(e);n&&(e.preventDefault(),e.stopPropagation(),t.$scope.$broadcast("md-calendar-parent-action",n))})},n.prototype.hideVerticalScrollbar=function(e){function t(){var t=n.width||o,i=n.scrollbarWidth,a=e.calendarScroller;r.style.width=t+"px",a.style.width=t+i+"px",a.style.paddingRight=i+"px"}var n=this,i=e.$element[0],r=i.querySelector(".md-calendar-scroll-mask");n.width>0?t():n.$$rAF(function(){var o=e.calendarScroller;n.scrollbarWidth=o.offsetWidth-o.clientWidth,n.width=i.querySelector("table").offsetWidth,t()})},n.prototype.getDateId=function(e,t){if(!t)throw new Error("A namespace for the date id has to be specified.");return["md",this.id,t,e.getFullYear(),e.getMonth(),e.getDate()].join("-")}}()}(),function(){!function(){function e(){return{template:'<table aria-hidden="true" class="md-calendar-day-header"><thead></thead></table><div class="md-calendar-scroll-mask"><md-virtual-repeat-container class="md-calendar-scroll-container" md-offset-size="'+(i-o)+'"><table role="grid" tabindex="0" class="md-calendar" aria-readonly="true"><tbody md-calendar-month-body role="rowgroup" md-virtual-repeat="i in monthCtrl.items" md-month-offset="$index" class="md-calendar-month" md-start-index="monthCtrl.getSelectedMonthIndex()" md-item-size="'+o+'"></tbody></table></md-virtual-repeat-container></div>',require:["^^mdCalendar","mdCalendarMonth"],controller:n,controllerAs:"monthCtrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1];r.initialize(i)}}}function n(e,t,n,o,i,r){this.$element=e,this.$scope=t,this.$animate=n,this.$q=o,this.dateUtil=i,this.dateLocale=r,this.calendarScroller=e[0].querySelector(".md-virtual-repeat-scroller"),this.firstRenderableDate=null,this.isInitialized=!1,this.isMonthTransitionInProgress=!1;var a=this;this.cellClickHandler=function(){var e=i.getTimestampFromNode(this);a.$scope.$apply(function(){a.calendarCtrl.setNgModelValue(e)})},this.headerClickHandler=function(){a.calendarCtrl.setCurrentView("year",i.getTimestampFromNode(this))}}t.module("material.components.datepicker").directive("mdCalendarMonth",e);var o=265,i=45;n.$inject=["$element","$scope","$animate","$q","$$mdDateUtil","$mdDateLocale"],n.prototype.initialize=function(e){var t=e.minDate,n=e.maxDate;if(this.calendarCtrl=e,this.items={length:2e3},n&&t){var o=this.dateUtil.getMonthDistance(t,n)+1;o=Math.max(o,1),o+=1,this.items.length=o}if(this.firstRenderableDate=this.dateUtil.incrementMonths(e.today,-this.items.length/2),t&&t>this.firstRenderableDate)this.firstRenderableDate=t;else if(n){this.items.length-2;this.firstRenderableDate=this.dateUtil.incrementMonths(n,-(this.items.length-2))}this.attachScopeListeners(),e.ngModelCtrl&&e.ngModelCtrl.$render()},n.prototype.getSelectedMonthIndex=function(){var e=this.calendarCtrl;return this.dateUtil.getMonthDistance(this.firstRenderableDate,e.displayDate||e.selectedDate||e.today)},n.prototype.changeSelectedDate=function(e){var t=this,n=t.calendarCtrl,o=n.selectedDate;n.selectedDate=e,this.changeDisplayDate(e).then(function(){var t=n.SELECTED_DATE_CLASS,i="month";if(o){var r=document.getElementById(n.getDateId(o,i));r&&(r.classList.remove(t),r.setAttribute("aria-selected","false"))}if(e){var a=document.getElementById(n.getDateId(e,i));a&&(a.classList.add(t),a.setAttribute("aria-selected","true"))}})},n.prototype.changeDisplayDate=function(e){if(!this.isInitialized)return this.buildWeekHeader(),this.calendarCtrl.hideVerticalScrollbar(this),this.isInitialized=!0,this.$q.when();if(!this.dateUtil.isValidDate(e)||this.isMonthTransitionInProgress)return this.$q.when();this.isMonthTransitionInProgress=!0;var t=this.animateDateChange(e);this.calendarCtrl.displayDate=e;var n=this;return t.then(function(){n.isMonthTransitionInProgress=!1}),t},n.prototype.animateDateChange=function(e){if(this.dateUtil.isValidDate(e)){var t=this.dateUtil.getMonthDistance(this.firstRenderableDate,e);this.calendarScroller.scrollTop=t*o}return this.$q.when()},n.prototype.buildWeekHeader=function(){for(var e=this.dateLocale.firstDayOfWeek,t=this.dateLocale.shortDays,n=document.createElement("tr"),o=0;7>o;o++){var i=document.createElement("th");i.textContent=t[(o+e)%7],n.appendChild(i)}this.$element.find("thead").append(n)},n.prototype.attachScopeListeners=function(){var e=this;e.$scope.$on("md-calendar-parent-changed",function(t,n){e.changeSelectedDate(n)}),e.$scope.$on("md-calendar-parent-action",t.bind(this,this.handleKeyEvent))},n.prototype.handleKeyEvent=function(e,t){var n=this.calendarCtrl,o=n.displayDate;if("select"===t)n.setNgModelValue(o);else{var i=null,r=this.dateUtil;switch(t){case"move-right":i=r.incrementDays(o,1);break;case"move-left":i=r.incrementDays(o,-1);break;case"move-page-down":i=r.incrementMonths(o,1);break;case"move-page-up":i=r.incrementMonths(o,-1);break;case"move-row-down":i=r.incrementDays(o,7);break;case"move-row-up":i=r.incrementDays(o,-7);break;case"start":i=r.getFirstDateOfMonth(o);break;case"end":i=r.getLastDateOfMonth(o)}i&&(i=this.dateUtil.clampDate(i,n.minDate,n.maxDate),this.changeDisplayDate(i).then(function(){n.focus(i)}))}}}()}(),function(){!function(){function e(){return{require:["^^mdCalendar","^^mdCalendarMonth","mdCalendarMonthBody"],scope:{offset:"=mdMonthOffset"},controller:n,controllerAs:"mdMonthBodyCtrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1],a=o[2];a.calendarCtrl=i,a.monthCtrl=r,a.generateContent(),e.$watch(function(){return a.offset},function(e,t){e!=t&&a.generateContent()})}}}function n(e,t,n){this.$element=e,this.dateUtil=t,this.dateLocale=n,this.monthCtrl=null,this.calendarCtrl=null,this.offset=null,this.focusAfterAppend=null}t.module("material.components.datepicker").directive("mdCalendarMonthBody",e),n.$inject=["$element","$$mdDateUtil","$mdDateLocale"],n.prototype.generateContent=function(){var e=this.dateUtil.incrementMonths(this.monthCtrl.firstRenderableDate,this.offset);this.$element.empty(),this.$element.append(this.buildCalendarForMonth(e)),this.focusAfterAppend&&(this.focusAfterAppend.classList.add(this.calendarCtrl.FOCUSED_DATE_CLASS),this.focusAfterAppend.focus(),this.focusAfterAppend=null)},n.prototype.buildDateCell=function(e){var t=this.monthCtrl,n=this.calendarCtrl,o=document.createElement("td");if(o.tabIndex=-1,o.classList.add("md-calendar-date"),o.setAttribute("role","gridcell"),e){o.setAttribute("tabindex","-1"),o.setAttribute("aria-label",this.dateLocale.longDateFormatter(e)),o.id=n.getDateId(e,"month"),o.setAttribute("data-timestamp",e.getTime()),this.dateUtil.isSameDay(e,n.today)&&o.classList.add(n.TODAY_CLASS),this.dateUtil.isValidDate(n.selectedDate)&&this.dateUtil.isSameDay(e,n.selectedDate)&&(o.classList.add(n.SELECTED_DATE_CLASS),o.setAttribute("aria-selected","true"));var i=this.dateLocale.dates[e.getDate()];if(this.isDateEnabled(e)){var r=document.createElement("span");r.classList.add("md-calendar-date-selection-indicator"),r.textContent=i,o.appendChild(r),o.addEventListener("click",t.cellClickHandler),n.displayDate&&this.dateUtil.isSameDay(e,n.displayDate)&&(this.focusAfterAppend=o)}else o.classList.add("md-calendar-date-disabled"),o.textContent=i}return o},n.prototype.isDateEnabled=function(e){return this.dateUtil.isDateWithinRange(e,this.calendarCtrl.minDate,this.calendarCtrl.maxDate)&&(!t.isFunction(this.calendarCtrl.dateFilter)||this.calendarCtrl.dateFilter(e))},n.prototype.buildDateRow=function(e){var t=document.createElement("tr");return t.setAttribute("role","row"),t.setAttribute("aria-label",this.dateLocale.weekNumberFormatter(e)),t},n.prototype.buildCalendarForMonth=function(e){var t=this.dateUtil.isValidDate(e)?e:new Date,n=this.dateUtil.getFirstDateOfMonth(t),o=this.getLocaleDay_(n),i=this.dateUtil.getNumberOfDaysInMonth(t),r=document.createDocumentFragment(),a=1,d=this.buildDateRow(a);r.appendChild(d);var s=this.offset===this.monthCtrl.items.length-1,c=0,l=document.createElement("td");if(l.textContent=this.dateLocale.monthHeaderFormatter(t),l.classList.add("md-calendar-month-label"),this.calendarCtrl.maxDate&&n>this.calendarCtrl.maxDate?l.classList.add("md-calendar-month-label-disabled"):(l.addEventListener("click",this.monthCtrl.headerClickHandler),l.setAttribute("data-timestamp",n.getTime()),l.setAttribute("aria-label",this.dateLocale.monthFormatter(t))),2>=o){l.setAttribute("colspan","7");var m=this.buildDateRow();if(m.appendChild(l),r.insertBefore(m,d),s)return r}else c=2,l.setAttribute("colspan","2"),d.appendChild(l);for(var u=c;o>u;u++)d.appendChild(this.buildDateCell());for(var h=o,p=n,f=1;i>=f;f++){if(7===h){if(s)return r;h=0,a++,d=this.buildDateRow(a),r.appendChild(d)}p.setDate(f);var g=this.buildDateCell(p);d.appendChild(g),h++}for(;d.childNodes.length<7;)d.appendChild(this.buildDateCell());for(;r.childNodes.length<6;){for(var b=this.buildDateRow(),v=0;7>v;v++)b.appendChild(this.buildDateCell());r.appendChild(b)}return r},n.prototype.getLocaleDay_=function(e){return(e.getDay()+(7-this.dateLocale.firstDayOfWeek))%7}}()}(),function(){!function(){function e(){return{template:'<div class="md-calendar-scroll-mask"><md-virtual-repeat-container class="md-calendar-scroll-container"><table role="grid" tabindex="0" class="md-calendar" aria-readonly="true"><tbody md-calendar-year-body role="rowgroup" md-virtual-repeat="i in yearCtrl.items" md-year-offset="$index" class="md-calendar-year" md-start-index="yearCtrl.getFocusedYearIndex()" md-item-size="'+o+'"></tbody></table></md-virtual-repeat-container></div>',require:["^^mdCalendar","mdCalendarYear"],controller:n,controllerAs:"yearCtrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1];r.initialize(i)}}}function n(e,t,n,o,i,r){this.$element=e,this.$scope=t,this.$animate=n,this.$q=o,this.dateUtil=i,this.$timeout=r,this.calendarScroller=e[0].querySelector(".md-virtual-repeat-scroller"),this.firstRenderableDate=null,this.isInitialized=!1,this.isMonthTransitionInProgress=!1;var a=this;this.cellClickHandler=function(){a.calendarCtrl.setCurrentView("month",i.getTimestampFromNode(this))}}t.module("material.components.datepicker").directive("mdCalendarYear",e);var o=88;n.$inject=["$element","$scope","$animate","$q","$$mdDateUtil","$timeout"],n.prototype.initialize=function(e){var t=e.minDate,n=e.maxDate;if(this.calendarCtrl=e,this.items={length:400},n&&t){var o=this.dateUtil.getYearDistance(t,n)+1;this.items.length=Math.max(o,1)}this.firstRenderableDate=this.dateUtil.incrementYears(e.today,-(this.items.length/2)),t&&t>this.firstRenderableDate?this.firstRenderableDate=t:n&&(this.firstRenderableDate=this.dateUtil.incrementMonths(n,-(this.items.length-1))),(t||n)&&this.$timeout(),this.attachScopeListeners(),e.ngModelCtrl&&e.ngModelCtrl.$render()},n.prototype.getFocusedYearIndex=function(){var e=this.calendarCtrl;return this.dateUtil.getYearDistance(this.firstRenderableDate,e.displayDate||e.selectedDate||e.today)},n.prototype.changeDate=function(e){if(!this.isInitialized)return this.calendarCtrl.hideVerticalScrollbar(this),this.isInitialized=!0,this.$q.when();if(this.dateUtil.isValidDate(e)&&!this.isMonthTransitionInProgress){var t=this,n=this.animateDateChange(e);return t.isMonthTransitionInProgress=!0,t.calendarCtrl.displayDate=e,n.then(function(){t.isMonthTransitionInProgress=!1})}},n.prototype.animateDateChange=function(e){if(this.dateUtil.isValidDate(e)){var t=this.dateUtil.getYearDistance(this.firstRenderableDate,e);this.calendarScroller.scrollTop=t*o}return this.$q.when()},n.prototype.handleKeyEvent=function(e,t){var n=this.calendarCtrl,o=n.displayDate;if("select"===t)this.changeDate(o).then(function(){n.setCurrentView("month",o),n.focus(o)});else{var i=null,r=this.dateUtil;switch(t){case"move-right":i=r.incrementMonths(o,1);break;case"move-left":i=r.incrementMonths(o,-1);break;case"move-row-down":i=r.incrementMonths(o,6);break;case"move-row-up":i=r.incrementMonths(o,-6)}if(i){var a=n.minDate?r.incrementMonths(r.getFirstDateOfMonth(n.minDate),1):null,d=n.maxDate?r.getFirstDateOfMonth(n.maxDate):null;i=r.getFirstDateOfMonth(this.dateUtil.clampDate(i,a,d)),this.changeDate(i).then(function(){n.focus(i)})}}},n.prototype.attachScopeListeners=function(){var e=this;e.$scope.$on("md-calendar-parent-changed",function(t,n){e.changeDate(n)}),e.$scope.$on("md-calendar-parent-action",t.bind(e,e.handleKeyEvent))}}()}(),function(){!function(){function e(){return{require:["^^mdCalendar","^^mdCalendarYear","mdCalendarYearBody"],scope:{offset:"=mdYearOffset"},controller:n,controllerAs:"mdYearBodyCtrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1],a=o[2];a.calendarCtrl=i,a.yearCtrl=r,a.generateContent(),e.$watch(function(){return a.offset},function(e,t){e!=t&&a.generateContent()})}}}function n(e,t,n){this.$element=e,this.dateUtil=t,this.dateLocale=n,this.calendarCtrl=null,this.yearCtrl=null,this.offset=null,this.focusAfterAppend=null}t.module("material.components.datepicker").directive("mdCalendarYearBody",e),n.$inject=["$element","$$mdDateUtil","$mdDateLocale"],n.prototype.generateContent=function(){var e=this.dateUtil.incrementYears(this.yearCtrl.firstRenderableDate,this.offset);this.$element.empty(),this.$element.append(this.buildCalendarForYear(e)),this.focusAfterAppend&&(this.focusAfterAppend.classList.add(this.calendarCtrl.FOCUSED_DATE_CLASS),this.focusAfterAppend.focus(),this.focusAfterAppend=null)},n.prototype.buildMonthCell=function(e,t){var n=this.calendarCtrl,o=this.yearCtrl,i=this.buildBlankCell(),r=new Date(e,t,1);i.setAttribute("aria-label",this.dateLocale.monthFormatter(r)),i.id=n.getDateId(r,"year"),i.setAttribute("data-timestamp",r.getTime()),this.dateUtil.isSameMonthAndYear(r,n.today)&&i.classList.add(n.TODAY_CLASS),this.dateUtil.isValidDate(n.selectedDate)&&this.dateUtil.isSameMonthAndYear(r,n.selectedDate)&&(i.classList.add(n.SELECTED_DATE_CLASS),i.setAttribute("aria-selected","true"));var a=this.dateLocale.shortMonths[t];if(this.dateUtil.isDateWithinRange(r,n.minDate,n.maxDate)){var d=document.createElement("span");d.classList.add("md-calendar-date-selection-indicator"),d.textContent=a,i.appendChild(d),i.addEventListener("click",o.cellClickHandler),n.displayDate&&this.dateUtil.isSameMonthAndYear(r,n.displayDate)&&(this.focusAfterAppend=i)}else i.classList.add("md-calendar-date-disabled"),i.textContent=a;return i},n.prototype.buildBlankCell=function(){var e=document.createElement("td");return e.tabIndex=-1,e.classList.add("md-calendar-date"),e.setAttribute("role","gridcell"),e.setAttribute("tabindex","-1"),e},n.prototype.buildCalendarForYear=function(e){var t,n=e.getFullYear(),o=document.createDocumentFragment(),i=document.createElement("tr"),r=document.createElement("td");for(r.className="md-calendar-month-label",r.textContent=n,i.appendChild(r),t=0;6>t;t++)i.appendChild(this.buildMonthCell(n,t));o.appendChild(i);var a=document.createElement("tr");for(a.appendChild(this.buildBlankCell()),t=6;12>t;t++)a.appendChild(this.buildMonthCell(n,t));return o.appendChild(a),o}}()}(),function(){!function(){t.module("material.components.datepicker").config(["$provide",function(e){function t(){this.months=null,this.shortMonths=null,this.days=null,this.shortDays=null,this.dates=null,this.firstDayOfWeek=0,this.formatDate=null,this.parseDate=null,this.monthHeaderFormatter=null,this.weekNumberFormatter=null,this.longDateFormatter=null,this.msgCalendar="",this.msgOpenCalendar=""}t.prototype.$get=function(e,t){function n(e){if(!e)return"";var n=e.toLocaleTimeString(),o=e;return 0!=e.getHours()||-1===n.indexOf("11:")&&-1===n.indexOf("23:")||(o=new Date(e.getFullYear(),e.getMonth(),e.getDate(),1,0,0)),t("date")(o,"M/d/yyyy")}function o(e){return new Date(e)}function i(e){e=e.trim();var t=/^(([a-zA-Z]{3,}|[0-9]{1,4})([ \.,]+|[\/\-])){2}([a-zA-Z]{3,}|[0-9]{1,4})$/;return t.test(e)}function r(e){return p.shortMonths[e.getMonth()]+" "+e.getFullYear()}function a(e){return p.months[e.getMonth()]+" "+e.getFullYear()}function d(e){return"Week "+e}function s(e){return[p.days[e.getDay()],p.months[e.getMonth()],p.dates[e.getDate()],e.getFullYear()].join(" ")}for(var c=e.DATETIME_FORMATS.DAY.map(function(e){return e[0]}),l=Array(32),m=1;31>=m;m++)l[m]=m;var u="Calendar",h="Open calendar",p={months:this.months||e.DATETIME_FORMATS.MONTH,shortMonths:this.shortMonths||e.DATETIME_FORMATS.SHORTMONTH,days:this.days||e.DATETIME_FORMATS.DAY,shortDays:this.shortDays||c,dates:this.dates||l,firstDayOfWeek:this.firstDayOfWeek||0,formatDate:this.formatDate||n,parseDate:this.parseDate||o,isDateComplete:this.isDateComplete||i,monthHeaderFormatter:this.monthHeaderFormatter||r,monthFormatter:this.monthFormatter||a,weekNumberFormatter:this.weekNumberFormatter||d,longDateFormatter:this.longDateFormatter||s,msgCalendar:this.msgCalendar||u,msgOpenCalendar:this.msgOpenCalendar||h};return p},t.prototype.$get.$inject=["$locale","$filter"],e.provider("$mdDateLocale",new t)}])}()}(),function(){!function(){function n(e){return{template:'<md-button class="md-datepicker-button md-icon-button" type="button" tabindex="-1" aria-hidden="true" ng-click="ctrl.openCalendarPane($event)"><md-icon class="md-datepicker-calendar-icon" aria-label="md-calendar" md-svg-src="'+e.mdCalendar+'"></md-icon></md-button><div class="md-datepicker-input-container" ng-class="{\'md-datepicker-focused\': ctrl.isFocused}"><input class="md-datepicker-input" aria-haspopup="true" ng-focus="ctrl.setFocused(true)" ng-blur="ctrl.setFocused(false)"><md-button type="button" md-no-ink class="md-datepicker-triangle-button md-icon-button" ng-click="ctrl.openCalendarPane($event)" aria-label="{{::ctrl.dateLocale.msgOpenCalendar}}"><div class="md-datepicker-expand-triangle"></div></md-button></div><div class="md-datepicker-calendar-pane md-whiteframe-z1"><div class="md-datepicker-input-mask"><div class="md-datepicker-input-mask-opaque"></div></div><div class="md-datepicker-calendar"><md-calendar role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" md-min-date="ctrl.minDate" md-max-date="ctrl.maxDate"md-date-filter="ctrl.dateFilter"ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen"></md-calendar></div></div>',require:["ngModel","mdDatepicker","?^mdInputContainer"],scope:{minDate:"=mdMinDate",maxDate:"=mdMaxDate",placeholder:"@mdPlaceholder",dateFilter:"=mdDateFilter"},controller:o,controllerAs:"ctrl",bindToController:!0,link:function(e,t,n,o){var i=o[0],r=o[1],a=o[2];if(a)throw Error("md-datepicker should not be placed inside md-input-container.");r.configureNgModel(i)}}}function o(e,n,o,i,r,a,d,s,c,l,m,u){this.$compile=i,this.$timeout=r,this.$window=a,this.dateLocale=l,this.dateUtil=m,this.$mdConstant=d,this.$mdUtil=c,this.$$rAF=u,this.documentElement=t.element(document.documentElement),this.ngModelCtrl=null,this.inputElement=n[0].querySelector("input"),this.ngInputElement=t.element(this.inputElement),this.inputContainer=n[0].querySelector(".md-datepicker-input-container"),this.calendarPane=n[0].querySelector(".md-datepicker-calendar-pane"),this.calendarButton=n[0].querySelector(".md-datepicker-button"),this.inputMask=n[0].querySelector(".md-datepicker-input-mask-opaque"),this.$element=n,this.$attrs=o,this.$scope=e,this.date=null,this.isFocused=!1,this.isDisabled,this.setDisabled(n[0].disabled||t.isString(o.disabled)),this.isCalendarOpen=!1,this.openOnFocus=o.hasOwnProperty("mdOpenOnFocus"),this.calendarPaneOpenedFrom=null,this.calendarPane.id="md-date-pane"+c.nextUid(),s(n),this.bodyClickHandler=t.bind(this,this.handleBodyClick),this.windowResizeHandler=c.debounce(t.bind(this,this.closeCalendarPane),100),o.tabindex||n.attr("tabindex","-1"),this.installPropertyInterceptors(),this.attachChangeListeners(),this.attachInteractionListeners();var h=this;e.$on("$destroy",function(){h.detachCalendarPane()})}t.module("material.components.datepicker").directive("mdDatepicker",n),n.$inject=["$$mdSvgRegistry"];var i=3,r="md-datepicker-invalid",a=500,d=368,s=360;o.$inject=["$scope","$element","$attrs","$compile","$timeout","$window","$mdConstant","$mdTheming","$mdUtil","$mdDateLocale","$$mdDateUtil","$$rAF"],o.prototype.configureNgModel=function(e){this.ngModelCtrl=e;var t=this;e.$render=function(){var e=t.ngModelCtrl.$viewValue;if(e&&!(e instanceof Date))throw Error("The ng-model for md-datepicker must be a Date instance. Currently the model is a: "+typeof e);t.date=e,t.inputElement.value=t.dateLocale.formatDate(e),t.resizeInputElement(),t.updateErrorState()}},o.prototype.attachChangeListeners=function(){var e=this;e.$scope.$on("md-calendar-change",function(t,n){e.ngModelCtrl.$setViewValue(n),e.date=n,e.inputElement.value=e.dateLocale.formatDate(n),e.closeCalendarPane(),e.resizeInputElement(),e.updateErrorState()}),e.ngInputElement.on("input",t.bind(e,e.resizeInputElement)),e.ngInputElement.on("input",e.$mdUtil.debounce(e.handleInputEvent,a,e))},o.prototype.attachInteractionListeners=function(){var e=this,n=this.$scope,o=this.$mdConstant.KEY_CODE;e.ngInputElement.on("keydown",function(t){t.altKey&&t.keyCode==o.DOWN_ARROW&&(e.openCalendarPane(t),n.$digest())}),e.openOnFocus&&e.ngInputElement.on("focus",t.bind(e,e.openCalendarPane)),n.$on("md-calendar-close",function(){e.closeCalendarPane()})},o.prototype.installPropertyInterceptors=function(){var e=this;if(this.$attrs.ngDisabled){var t=this.$scope.$parent; +t&&t.$watch(this.$attrs.ngDisabled,function(t){e.setDisabled(t)})}Object.defineProperty(this,"placeholder",{get:function(){return e.inputElement.placeholder},set:function(t){e.inputElement.placeholder=t||""}})},o.prototype.setDisabled=function(e){this.isDisabled=e,this.inputElement.disabled=e,this.calendarButton.disabled=e},o.prototype.updateErrorState=function(e){var n=e||this.date;if(this.clearErrorState(),this.dateUtil.isValidDate(n)){if(n=this.dateUtil.createDateAtMidnight(n),this.dateUtil.isValidDate(this.minDate)){var o=this.dateUtil.createDateAtMidnight(this.minDate);this.ngModelCtrl.$setValidity("mindate",n>=o)}if(this.dateUtil.isValidDate(this.maxDate)){var i=this.dateUtil.createDateAtMidnight(this.maxDate);this.ngModelCtrl.$setValidity("maxdate",i>=n)}t.isFunction(this.dateFilter)&&this.ngModelCtrl.$setValidity("filtered",this.dateFilter(n))}else this.ngModelCtrl.$setValidity("valid",null==n);this.ngModelCtrl.$valid||this.inputContainer.classList.add(r)},o.prototype.clearErrorState=function(){this.inputContainer.classList.remove(r),["mindate","maxdate","filtered","valid"].forEach(function(e){this.ngModelCtrl.$setValidity(e,!0)},this)},o.prototype.resizeInputElement=function(){this.inputElement.size=this.inputElement.value.length+i},o.prototype.handleInputEvent=function(){var e=this.inputElement.value,t=e?this.dateLocale.parseDate(e):null;this.dateUtil.setDateTimeToMidnight(t);var n=""==e||this.dateUtil.isValidDate(t)&&this.dateLocale.isDateComplete(e)&&this.isDateEnabled(t);n&&(this.ngModelCtrl.$setViewValue(t),this.date=t),this.updateErrorState(t)},o.prototype.isDateEnabled=function(e){return this.dateUtil.isDateWithinRange(e,this.minDate,this.maxDate)&&(!t.isFunction(this.dateFilter)||this.dateFilter(e))},o.prototype.attachCalendarPane=function(){var e=this.calendarPane,n=document.body;e.style.transform="",this.$element.addClass("md-datepicker-open"),t.element(n).addClass("md-datepicker-is-showing");var o=this.inputContainer.getBoundingClientRect(),i=n.getBoundingClientRect(),r=o.top-i.top,a=o.left-i.left,c=i.top<0&&0==document.body.scrollTop?-i.top:document.body.scrollTop,l=i.left<0&&0==document.body.scrollLeft?-i.left:document.body.scrollLeft,m=c+this.$window.innerHeight,u=l+this.$window.innerWidth;if(a+s>u){if(u-s>0)a=u-s;else{a=l;var h=this.$window.innerWidth/s;e.style.transform="scale("+h+")"}e.classList.add("md-datepicker-pos-adjusted")}r+d>m&&m-d>c&&(r=m-d,e.classList.add("md-datepicker-pos-adjusted")),e.style.left=a+"px",e.style.top=r+"px",document.body.appendChild(e),this.inputMask.style.left=o.width+"px",this.$$rAF(function(){e.classList.add("md-pane-open")})},o.prototype.detachCalendarPane=function(){this.$element.removeClass("md-datepicker-open"),t.element(document.body).removeClass("md-datepicker-is-showing"),this.calendarPane.classList.remove("md-pane-open"),this.calendarPane.classList.remove("md-datepicker-pos-adjusted"),this.isCalendarOpen&&this.$mdUtil.enableScrolling(),this.calendarPane.parentNode&&this.calendarPane.parentNode.removeChild(this.calendarPane)},o.prototype.openCalendarPane=function(t){if(!this.isCalendarOpen&&!this.isDisabled){this.isCalendarOpen=!0,this.calendarPaneOpenedFrom=t.target,this.$mdUtil.disableScrollAround(this.calendarPane),this.attachCalendarPane(),this.focusCalendar();var n=this;this.$mdUtil.nextTick(function(){n.documentElement.on("click touchstart",n.bodyClickHandler)},!1),e.addEventListener("resize",this.windowResizeHandler)}},o.prototype.closeCalendarPane=function(){function t(){n.detachCalendarPane(),n.isCalendarOpen=!1,n.ngModelCtrl.$setTouched(),n.documentElement.off("click touchstart",n.bodyClickHandler),e.removeEventListener("resize",n.windowResizeHandler)}if(this.isCalendarOpen){var n=this;n.calendarPaneOpenedFrom.focus(),n.calendarPaneOpenedFrom=null,n.openOnFocus?this.$mdUtil.nextTick(t):t()}},o.prototype.getCalendarCtrl=function(){return t.element(this.calendarPane.querySelector("md-calendar")).controller("mdCalendar")},o.prototype.focusCalendar=function(){var e=this;this.$mdUtil.nextTick(function(){e.getCalendarCtrl().focus()},!1)},o.prototype.setFocused=function(e){e||this.ngModelCtrl.$setTouched(),this.isFocused=e},o.prototype.handleBodyClick=function(e){if(this.isCalendarOpen){var t=this.$mdUtil.getClosest,n=t(e.target,"md-calendar-year")||t(e.target,"md-calendar-month");n||this.closeCalendarPane(),this.$scope.$digest()}}}()}(),function(){!function(){t.module("material.components.datepicker").factory("$$mdDateUtil",function(){function e(e){return new Date(e.getFullYear(),e.getMonth(),1)}function n(e){return new Date(e.getFullYear(),e.getMonth()+1,0).getDate()}function o(e){return new Date(e.getFullYear(),e.getMonth()+1,1)}function i(e){return new Date(e.getFullYear(),e.getMonth()-1,1)}function r(e,t){return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()}function a(e,t){return e.getDate()==t.getDate()&&r(e,t)}function d(e,t){var n=o(e);return r(n,t)}function s(e,t){var n=i(e);return r(t,n)}function c(e,t){return b((e.getTime()+t.getTime())/2)}function l(t){var n=e(t);return Math.floor((n.getDay()+t.getDate()-1)/7)}function m(e,t){return new Date(e.getFullYear(),e.getMonth(),e.getDate()+t)}function u(e,t){var o=new Date(e.getFullYear(),e.getMonth()+t,1),i=n(o);return i<e.getDate()?o.setDate(i):o.setDate(e.getDate()),o}function h(e,t){return 12*(t.getFullYear()-e.getFullYear())+(t.getMonth()-e.getMonth())}function p(e){return new Date(e.getFullYear(),e.getMonth(),n(e))}function f(e){return null!=e&&e.getTime&&!isNaN(e.getTime())}function g(e){f(e)&&e.setHours(0,0,0,0)}function b(e){var n;return n=t.isUndefined(e)?new Date:new Date(e),g(n),n}function v(e,t,n){var o=b(e),i=f(t)?b(t):null,r=f(n)?b(n):null;return(!i||o>=i)&&(!r||r>=o)}function E(e,t){return u(e,12*t)}function $(e,t){return t.getFullYear()-e.getFullYear()}function C(e,t,n){var o=e;return t&&t>e&&(o=new Date(t.getTime())),n&&e>n&&(o=new Date(n.getTime())),o}function y(e){return e&&e.hasAttribute("data-timestamp")?Number(e.getAttribute("data-timestamp")):void 0}return{getFirstDateOfMonth:e,getNumberOfDaysInMonth:n,getDateInNextMonth:o,getDateInPreviousMonth:i,isInNextMonth:d,isInPreviousMonth:s,getDateMidpoint:c,isSameMonthAndYear:r,getWeekOfMonth:l,incrementDays:m,incrementMonths:u,getLastDateOfMonth:p,isSameDay:a,getMonthDistance:h,isValidDate:f,setDateTimeToMidnight:g,createDateAtMidnight:b,isDateWithinRange:v,incrementYears:E,getYearDistance:$,clampDate:C,getTimestampFromNode:y}})}()}(),function(){function e(e,n,o){return{restrict:"E",link:function(i,r){r.addClass("_md"),n(r),e(function(){function e(){r.toggleClass("md-content-overflow",a.scrollHeight>a.clientHeight)}var n,a=r[0].querySelector("md-dialog-content");a&&(n=a.getElementsByTagName("img"),e(),t.element(n).on("load",e)),i.$on("$destroy",function(){o.destroy(r)})})}}}function o(e){function o(e,t,n){return{template:['<md-dialog md-theme="{{ dialog.theme }}" aria-label="{{ dialog.ariaLabel }}" ng-class="dialog.css">',' <md-dialog-content class="md-dialog-content" role="document" tabIndex="-1">',' <h2 class="md-title">{{ dialog.title }}</h2>',' <div ng-if="::dialog.mdHtmlContent" class="_md-dialog-content-body" ',' ng-bind-html="::dialog.mdHtmlContent"></div>',' <div ng-if="::!dialog.mdHtmlContent" class="_md-dialog-content-body">'," <p>{{::dialog.mdTextContent}}</p>"," </div>",' <md-input-container md-no-float ng-if="::dialog.$type == \'prompt\'" class="md-prompt-input-container">',' <input ng-keypress="dialog.keypress($event)" md-autofocus ng-model="dialog.result" placeholder="{{::dialog.placeholder}}">'," </md-input-container>"," </md-dialog-content>"," <md-dialog-actions>",' <md-button ng-if="dialog.$type === \'confirm\' || dialog.$type === \'prompt\'" ng-click="dialog.abort()" class="md-primary">'," {{ dialog.cancel }}"," </md-button>",' <md-button ng-click="dialog.hide()" class="md-primary" md-autofocus="dialog.$type===\'alert\'">'," {{ dialog.ok }}"," </md-button>"," </md-dialog-actions>","</md-dialog>"].join("").replace(/\s\s+/g,""),controller:function(){var t="prompt"==this.$type;t&&this.initialValue&&(this.result=this.initialValue),this.hide=function(){e.hide(t?this.result:!0)},this.abort=function(){e.cancel()},this.keypress=function(t){t.keyCode===n.KEY_CODE.ENTER&&e.hide(this.result)}},controllerAs:"dialog",bindToController:!0,theme:t.defaultTheme()}}function i(e,o,i,d,s,c,l,m,u,h){function p(e,t,n,o){if(o){if(o.mdHtmlContent=o.htmlContent||n.htmlContent||"",o.mdTextContent=o.textContent||n.textContent||o.content||n.content||"",o.mdHtmlContent&&!h.has("$sanitize"))throw Error("The ngSanitize module must be loaded in order to use htmlContent.");if(o.mdHtmlContent&&o.mdTextContent)throw Error("md-dialog cannot have both `htmlContent` and `textContent`")}}function f(e,n,o,r){function a(){n[0].querySelector(".md-actions")&&u.warn("Using a class of md-actions is deprecated, please use <md-dialog-actions>.")}function d(){function e(){var e=n[0].querySelector(".dialog-close");if(!e){var o=n[0].querySelectorAll(".md-actions button, md-dialog-actions button");e=o[o.length-1]}return t.element(e)}if(o.focusOnOpen){var r=i.findFocusTarget(n)||e();r.focus()}}if(t.element(c[0].body).addClass("md-dialog-is-showing"),o.contentElement){var s=o.contentElement;t.isString(s)?(s=document.querySelector(s),o.elementInsertionSibling=s.nextElementSibling,o.elementInsertionParent=s.parentNode):(s=s[0]||s,document.contains(s)&&(o.elementInsertionSibling=s.nextElementSibling,o.elementInsertionParent=s.parentNode)),o.elementInsertionEntry=s,n=t.element(s)}return b(o),$(n.find("md-dialog"),o),E(e,n,o),M(n,o).then(function(){v(n,o),C(n,o),a(),d()})}function g(e,n,o){function i(){return _(n,o)}function d(){o.contentElement&&(o.reverseContainerStretch(),o.elementInsertionParent?o.elementInsertionSibling?o.elementInsertionParent.insertBefore(o.elementInsertionEntry,o.elementInsertionSibling):o.elementInsertionParent.appendChild(o.elementInsertionEntry):o.elementInsertionEntry.parentNode.removeChild(o.elementInsertionEntry))}function s(){t.element(c[0].body).removeClass("md-dialog-is-showing"),o.contentElement?d():n.remove(),o.$destroy||o.origin.focus()}return o.deactivateListeners(),o.unlockScreenReader(),o.hideBackdrop(o.$destroy),r&&r.parentNode&&r.parentNode.removeChild(r),a&&a.parentNode&&a.parentNode.removeChild(a),o.$destroy?s():i().then(s)}function b(e){function o(e,o){var i=t.element(e||{});if(i&&i.length){var r={top:0,left:0,height:0,width:0},a=t.isFunction(i[0].getBoundingClientRect);return t.extend(o||{},{element:a?i:n,bounds:a?i[0].getBoundingClientRect():t.extend({},r,i[0]),focus:t.bind(i,i.focus)})}}function i(e,n){return t.isString(e)&&(e=c[0].querySelector(e)),t.element(e||n)}e.origin=t.extend({element:null,bounds:null,focus:t.noop},e.origin||{}),e.parent=i(e.parent,m),e.closeTo=o(i(e.closeTo)),e.openFrom=o(i(e.openFrom)),e.targetEvent&&(e.origin=o(e.targetEvent.target,e.origin))}function v(n,o){var r=t.element(l),a=i.debounce(function(){y(n,o)},60),s=[],c=function(){var t="alert"==o.$type?e.hide:e.cancel;i.nextTick(t,!0)};if(o.escapeToClose){var m=o.parent,u=function(e){e.keyCode===d.KEY_CODE.ESCAPE&&(e.stopPropagation(),e.preventDefault(),c())};n.on("keydown",u),m.on("keydown",u),s.push(function(){n.off("keydown",u),m.off("keydown",u)})}if(r.on("resize",a),s.push(function(){r.off("resize",a)}),o.clickOutsideToClose){var h,p=n,f=function(e){h=e.target},g=function(e){h===p[0]&&e.target===p[0]&&(e.stopPropagation(),e.preventDefault(),c())};p.on("mousedown",f),p.on("mouseup",g),s.push(function(){p.off("mousedown",f),p.off("mouseup",g)})}o.deactivateListeners=function(){s.forEach(function(e){e()}),o.deactivateListeners=null}}function E(e,t,n){n.disableParentScroll&&(n.restoreScroll=i.disableScrollAround(t,n.parent)),n.hasBackdrop&&(n.backdrop=i.createBackdrop(e,"_md-dialog-backdrop md-opaque"),s.enter(n.backdrop,n.parent)),n.hideBackdrop=function(e){n.backdrop&&(e?n.backdrop.remove():s.leave(n.backdrop)),n.disableParentScroll&&(n.restoreScroll(),delete n.restoreScroll),n.hideBackdrop=null}}function $(e,t){var n="alert"===t.$type?"alertdialog":"dialog",d=e.find("md-dialog-content"),s=e.attr("id"),c="dialogContent_"+(s||i.nextUid());e.attr({role:n,tabIndex:"-1"}),0===d.length&&(d=e,s&&(c=s)),d.attr("id",c),e.attr("aria-describedby",c),t.ariaLabel?o.expect(e,"aria-label",t.ariaLabel):o.expectAsync(e,"aria-label",function(){var e=d.text().split(/\s+/);return e.length>3&&(e=e.slice(0,3).concat("...")),e.join(" ")}),r=document.createElement("div"),r.classList.add("_md-dialog-focus-trap"),r.tabIndex=0,a=r.cloneNode(!1);var l=function(){e.focus()};r.addEventListener("focus",l),a.addEventListener("focus",l),e[0].parentNode.insertBefore(r,e[0]),e.after(a)}function C(e,t){function n(e){for(;e.parentNode;){if(e===document.body)return;for(var t=e.parentNode.children,i=0;i<t.length;i++)e===t[i]||A(t[i],["SCRIPT","STYLE"])||t[i].setAttribute("aria-hidden",o);n(e=e.parentNode)}}var o=!0;n(e[0]),t.unlockScreenReader=function(){o=!1,n(e[0]),t.unlockScreenReader=null}}function y(e,t){var n="fixed"==l.getComputedStyle(c[0].body).position,o=t.backdrop?l.getComputedStyle(t.backdrop[0]):null,r=o?Math.min(c[0].body.clientHeight,Math.ceil(Math.abs(parseInt(o.height,10)))):0,a={top:e.css("top"),height:e.css("height")};return e.css({top:(n?i.scrollTop(t.parent):0)+"px",height:r?r+"px":"100%"}),function(){e.css(a)}}function M(e,t){t.parent.append(e),t.reverseContainerStretch=y(e,t);var n=e.find("md-dialog"),o=i.dom.animator,r=o.calculateZoomToOrigin,a={transitionInClass:"_md-transition-in",transitionOutClass:"_md-transition-out"},d=o.toTransformCss(r(n,t.openFrom||t.origin)),s=o.toTransformCss("");return t.fullscreen&&n.addClass("md-dialog-fullscreen"),o.translate3d(n,d,s,a).then(function(e){return t.reverseAnimate=function(){return delete t.reverseAnimate,t.closeTo?(a={transitionInClass:"_md-transition-out",transitionOutClass:"_md-transition-in"},d=s,s=o.toTransformCss(r(n,t.closeTo)),o.translate3d(n,d,s,a)):e(s=o.toTransformCss(r(n,t.origin)))},t.clearAnimate=function(){return delete t.clearAnimate,o.translate3d(n,s,o.toTransformCss(""),{})},!0})}function _(e,t){return t.reverseAnimate().then(function(){t.contentElement&&t.clearAnimate()})}function A(e,t){return-1!==t.indexOf(e.nodeName)?!0:void 0}return{hasBackdrop:!0,isolateScope:!0,onShow:f,onShowing:p,onRemove:g,clickOutsideToClose:!1,escapeToClose:!0,targetEvent:null,contentElement:null,closeTo:null,openFrom:null,focusOnOpen:!0,disableParentScroll:!0,autoWrap:!0,fullscreen:!1,transformTemplate:function(e,t){function n(e){return t.autoWrap&&!/<\/md-dialog>/g.test(e)?"<md-dialog>"+(e||"")+"</md-dialog>":e||""}return'<div class="md-dialog-container" tabindex="-1">'+n(e)+"</div>"}}}var r,a;return o.$inject=["$mdDialog","$mdTheming","$mdConstant"],i.$inject=["$mdDialog","$mdAria","$mdUtil","$mdConstant","$animate","$document","$window","$rootElement","$log","$injector"],e("$mdDialog").setDefaults({methods:["disableParentScroll","hasBackdrop","clickOutsideToClose","escapeToClose","targetEvent","closeTo","openFrom","parent","fullscreen","contentElement"],options:i}).addPreset("alert",{methods:["title","htmlContent","textContent","content","ariaLabel","ok","theme","css"],options:o}).addPreset("confirm",{methods:["title","htmlContent","textContent","content","ariaLabel","ok","cancel","theme","css"],options:o}).addPreset("prompt",{methods:["title","htmlContent","textContent","initialValue","content","placeholder","ariaLabel","ok","cancel","theme","css"],options:o})}t.module("material.components.dialog",["material.core","material.components.backdrop"]).directive("mdDialog",e).provider("$mdDialog",o),e.$inject=["$$rAF","$mdTheming","$mdDialog"],o.$inject=["$$interimElementProvider"]}(),function(){function e(e){return{restrict:"E",link:e}}t.module("material.components.divider",["material.core"]).directive("mdDivider",e),e.$inject=["$mdTheming"]}(),function(){!function(){function e(e){return{restrict:"E",require:["^?mdFabSpeedDial","^?mdFabToolbar"],compile:function(t,n){var o=t.children(),i=e.prefixer().hasAttribute(o,"ng-repeat");i?o.addClass("md-fab-action-item"):o.wrap('<div class="md-fab-action-item">')}}}t.module("material.components.fabActions",["material.core"]).directive("mdFabActions",e),e.$inject=["$mdUtil"]}()}(),function(){!function(){function e(e,n,o,i,r,a){function d(){N.direction=N.direction||"down",N.isOpen=N.isOpen||!1,l(),n.addClass("_md-animations-waiting")}function s(){var o=["click","focusin","focusout"];t.forEach(o,function(e){n.on(e,c)}),e.$on("$destroy",function(){t.forEach(o,function(e){n.off(e,c)}),p()})}function c(e){"click"==e.type&&w(e),"focusout"!=e.type||D||(D=a(function(){N.close()},100,!1)),"focusin"==e.type&&D&&(a.cancel(D),D=null)}function l(){N.currentActionIndex=-1}function m(){e.$watch("vm.direction",function(e,t){o.removeClass(n,"md-"+t),o.addClass(n,"md-"+e),l()});var t,i;e.$watch("vm.isOpen",function(e){l(),t&&i||(t=k(),i=x()),e?h():p();var r=e?"md-is-open":"",a=e?"":"md-is-open";t.attr("aria-haspopup",!0),t.attr("aria-expanded",e),i.attr("aria-hidden",!e),o.setClass(n,r,a)})}function u(){n[0].scrollHeight>0?o.addClass(n,"_md-animations-ready").then(function(){n.removeClass("_md-animations-waiting")}):10>S&&(a(u,100),S+=1)}function h(){n.on("keydown",g),i.nextTick(function(){t.element(document).on("click touchend",f)})}function p(){n.off("keydown",g),t.element(document).off("click touchend",f)}function f(e){if(e.target){var t=i.getClosest(e.target,"md-fab-trigger"),n=i.getClosest(e.target,"md-fab-actions");t||n||N.close()}}function g(e){switch(e.which){case r.KEY_CODE.ESCAPE:return N.close(),e.preventDefault(),!1;case r.KEY_CODE.LEFT_ARROW:return C(e),!1;case r.KEY_CODE.UP_ARROW:return y(e),!1;case r.KEY_CODE.RIGHT_ARROW:return M(e),!1;case r.KEY_CODE.DOWN_ARROW:return _(e),!1}}function b(e){E(e,-1)}function v(e){E(e,1)}function E(e,n){var o=$();N.currentActionIndex=N.currentActionIndex+n,N.currentActionIndex=Math.min(o.length-1,N.currentActionIndex),N.currentActionIndex=Math.max(0,N.currentActionIndex);var i=t.element(o[N.currentActionIndex]).children()[0];t.element(i).attr("tabindex",0),i.focus(),e.preventDefault(),e.stopImmediatePropagation()}function $(){var e=x()[0].querySelectorAll(".md-fab-action-item");return t.forEach(e,function(e){t.element(t.element(e).children()[0]).attr("tabindex",-1)}),e}function C(e){"left"===N.direction?v(e):b(e)}function y(e){"down"===N.direction?b(e):v(e)}function M(e){"left"===N.direction?b(e):v(e)}function _(e){"up"===N.direction?b(e):v(e)}function A(e){return i.getClosest(e,"md-fab-trigger")}function T(e){return i.getClosest(e,"md-fab-actions")}function w(e){A(e.target)&&N.toggle(),T(e.target)&&N.close()}function k(){return n.find("md-fab-trigger")}function x(){return n.find("md-fab-actions")}var N=this;N.open=function(){e.$evalAsync("vm.isOpen = true")},N.close=function(){e.$evalAsync("vm.isOpen = false"),n.find("md-fab-trigger")[0].focus()},N.toggle=function(){e.$evalAsync("vm.isOpen = !vm.isOpen")},d(),s(),m();var S=0;u();var D}t.module("material.components.fabShared",["material.core"]).controller("MdFabController",e),e.$inject=["$scope","$element","$animate","$mdUtil","$mdConstant","$timeout"]}()}(),function(){!function(){function n(){function e(e,t){t.prepend('<div class="_md-css-variables"></div>')}return{restrict:"E",scope:{direction:"@?mdDirection",isOpen:"=?mdOpen"},bindToController:!0,controller:"MdFabController",controllerAs:"vm",link:e}}function o(n){function o(e){n(e,r,!1)}function i(n){if(!n.hasClass("_md-animations-waiting")||n.hasClass("_md-animations-ready")){var o=n[0],i=n.controller("mdFabSpeedDial"),r=o.querySelectorAll(".md-fab-action-item"),a=o.querySelector("md-fab-trigger"),d=o.querySelector("._md-css-variables"),s=parseInt(e.getComputedStyle(d).zIndex);t.forEach(r,function(e,t){var n=e.style;n.transform=n.webkitTransform="",n.transitionDelay="",n.opacity=1,n.zIndex=r.length-t+s}),a.style.zIndex=s+r.length+1,i.isOpen||t.forEach(r,function(e,t){var n,o,r=e.style,d=(a.clientHeight-e.clientHeight)/2,s=(a.clientWidth-e.clientWidth)/2;switch(i.direction){case"up":n=e.scrollHeight*(t+1)+d,o="Y";break;case"down":n=-(e.scrollHeight*(t+1)+d),o="Y";break;case"left":n=e.scrollWidth*(t+1)+s,o="X";break;case"right":n=-(e.scrollWidth*(t+1)+s),o="X"}var c="translate"+o+"("+n+"px)";r.transform=r.webkitTransform=c})}}return{addClass:function(e,t,n){e.hasClass("md-fling")?(i(e),o(n)):n()},removeClass:function(e,t,n){i(e),o(n)}}}function i(n){function o(e){n(e,r,!1)}function i(n){var o=n[0],i=n.controller("mdFabSpeedDial"),r=o.querySelectorAll(".md-fab-action-item"),d=o.querySelector("._md-css-variables"),s=parseInt(e.getComputedStyle(d).zIndex);t.forEach(r,function(e,t){var n=e.style,o=t*a;n.opacity=i.isOpen?1:0,n.transform=n.webkitTransform=i.isOpen?"scale(1)":"scale(0)",n.transitionDelay=(i.isOpen?o:r.length-o)+"ms",n.zIndex=r.length-t+s})}var a=65;return{addClass:function(e,t,n){i(e),o(n)},removeClass:function(e,t,n){i(e),o(n)}}}var r=300;t.module("material.components.fabSpeedDial",["material.core","material.components.fabShared","material.components.fabTrigger","material.components.fabActions"]).directive("mdFabSpeedDial",n).animation(".md-fling",o).animation(".md-scale",i).service("mdFabSpeedDialFlingAnimation",o).service("mdFabSpeedDialScaleAnimation",i),o.$inject=["$timeout"],i.$inject=["$timeout"]}()}(),function(){!function(){function n(){function e(e,t,n){t.addClass("md-fab-toolbar"),t.find("md-fab-trigger").find("button").prepend('<div class="_md-fab-toolbar-background"></div>')}return{restrict:"E",transclude:!0,template:'<div class="_md-fab-toolbar-wrapper"> <div class="_md-fab-toolbar-content" ng-transclude></div></div>',scope:{direction:"@?mdDirection",isOpen:"=?mdOpen"},bindToController:!0,controller:"MdFabController",controllerAs:"vm",link:e}}function o(){function n(n,o,i){if(o){var r=n[0],a=n.controller("mdFabToolbar"),d=r.querySelector("._md-fab-toolbar-background"),s=r.querySelector("md-fab-trigger button"),c=r.querySelector("md-toolbar"),l=r.querySelector("md-fab-trigger button md-icon"),m=n.find("md-fab-actions").children();if(s&&d){var u=e.getComputedStyle(s).getPropertyValue("background-color"),h=r.offsetWidth,p=(r.offsetHeight,2*(h/s.offsetWidth));d.style.backgroundColor=u,d.style.borderRadius=h+"px",a.isOpen?(c.style.pointerEvents="inherit",d.style.width=s.offsetWidth+"px",d.style.height=s.offsetHeight+"px",d.style.transform="scale("+p+")",d.style.transitionDelay="0ms",l&&(l.style.transitionDelay=".3s"),t.forEach(m,function(e,t){e.style.transitionDelay=25*(m.length-t)+"ms"})):(c.style.pointerEvents="none",d.style.transform="scale(1)",d.style.top="0",n.hasClass("md-right")&&(d.style.left="0",d.style.right=null),n.hasClass("md-left")&&(d.style.right="0",d.style.left=null),d.style.transitionDelay="200ms",l&&(l.style.transitionDelay="0ms"),t.forEach(m,function(e,t){e.style.transitionDelay=200+25*t+"ms"}))}}}return{addClass:function(e,t,o){n(e,t,o),o()},removeClass:function(e,t,o){n(e,t,o),o()}}}t.module("material.components.fabToolbar",["material.core","material.components.fabShared","material.components.fabTrigger","material.components.fabActions"]).directive("mdFabToolbar",n).animation(".md-fab-toolbar",o).service("mdFabToolbarAnimation",o)}()}(),function(){!function(){function e(){return{restrict:"E",require:["^?mdFabSpeedDial","^?mdFabToolbar"]}}t.module("material.components.fabTrigger",["material.core"]).directive("mdFabTrigger",e)}()}(),function(){t.module("material.components.icon",["material.core"])}(),function(){function e(e,o,i,r){function a(n,a,d,s){function c(){for(var e in o.MEDIA)r(e),r.getQuery(o.MEDIA[e]).addListener(M);return r.watchResponsiveAttributes(["md-cols","md-row-height","md-gutter"],d,m)}function l(){s.layoutDelegate=t.noop,_();for(var e in o.MEDIA)r.getQuery(o.MEDIA[e]).removeListener(M)}function m(e){null==e?s.invalidateLayout():r(e)&&s.invalidateLayout()}function u(e){var o=g(),r={tileSpans:b(o),colCount:v(),rowMode:C(),rowHeight:$(),gutter:E()};if(e||!t.equals(r,A)){var d=i(r.colCount,r.tileSpans,o).map(function(e,n){return{grid:{element:a,style:f(r.colCount,n,r.gutter,r.rowMode,r.rowHeight)},tiles:e.map(function(e,i){return{element:t.element(o[i]),style:p(e.position,e.spans,r.colCount,n,r.gutter,r.rowMode,r.rowHeight)}})}}).reflow().performance();n.mdOnLayout({$event:{performance:d}}),A=r}}function h(e){return T+e+w}function p(e,t,n,o,i,r,a){var d=1/n*100,s=(n-1)/n,c=k({share:d,gutterShare:s,gutter:i}),l={left:x({unit:c,offset:e.col,gutter:i}),width:N({unit:c,span:t.col,gutter:i}),paddingTop:"",marginTop:"",top:"",height:""};switch(r){case"fixed":l.top=x({unit:a,offset:e.row,gutter:i}),l.height=N({unit:a,span:t.row,gutter:i});break;case"ratio":var m=d/a,u=k({share:m,gutterShare:s,gutter:i});l.paddingTop=N({unit:u,span:t.row,gutter:i}),l.marginTop=x({unit:u,offset:e.row,gutter:i});break;case"fit":var h=(o-1)/o,m=1/o*100,u=k({share:m,gutterShare:h,gutter:i});l.top=x({unit:u,offset:e.row,gutter:i}),l.height=N({unit:u,span:t.row,gutter:i})}return l}function f(e,t,n,o,i){var r={};switch(o){case"fixed":r.height=N({unit:i,span:t,gutter:n}),r.paddingBottom="";break;case"ratio":var a=1===e?0:(e-1)/e,d=1/e*100,s=d*(1/i),c=k({share:s,gutterShare:a,gutter:n});r.height="",r.paddingBottom=N({unit:c,span:t,gutter:n});break;case"fit":}return r}function g(){return[].filter.call(a.children(),function(e){return"MD-GRID-TILE"==e.tagName&&!e.$$mdDestroyed})}function b(e){return[].map.call(e,function(e){var n=t.element(e).controller("mdGridTile");return{row:parseInt(r.getResponsiveAttribute(n.$attrs,"md-rowspan"),10)||1,col:parseInt(r.getResponsiveAttribute(n.$attrs,"md-colspan"),10)||1}})}function v(){var e=parseInt(r.getResponsiveAttribute(d,"md-cols"),10);if(isNaN(e))throw"md-grid-list: md-cols attribute was not found, or contained a non-numeric value";return e}function E(){return y(r.getResponsiveAttribute(d,"md-gutter")||1)}function $(){var e=r.getResponsiveAttribute(d,"md-row-height");if(!e)throw"md-grid-list: md-row-height attribute was not found";switch(C()){case"fixed":return y(e);case"ratio":var t=e.split(":");return parseFloat(t[0])/parseFloat(t[1]);case"fit":return 0}}function C(){var e=r.getResponsiveAttribute(d,"md-row-height");if(!e)throw"md-grid-list: md-row-height attribute was not found";return"fit"==e?"fit":-1!==e.indexOf(":")?"ratio":"fixed"}function y(e){return/\D$/.test(e)?e:e+"px"}a.addClass("_md"),a.attr("role","list"),s.layoutDelegate=u;var M=t.bind(s,s.invalidateLayout),_=c();n.$on("$destroy",l);var A,T=e.startSymbol(),w=e.endSymbol(),k=e(h("share")+"% - ("+h("gutter")+" * "+h("gutterShare")+")"),x=e("calc(("+h("unit")+" + "+h("gutter")+") * "+h("offset")+")"),N=e("calc(("+h("unit")+") * "+h("span")+" + ("+h("span")+" - 1) * "+h("gutter")+")")}return{restrict:"E",controller:n,scope:{mdOnLayout:"&"},link:a}}function n(e){this.layoutInvalidated=!1,this.tilesInvalidated=!1,this.$timeout_=e.nextTick,this.layoutDelegate=t.noop}function o(e){function n(t,n){var o,a,d,s,c,l;return s=e.time(function(){a=i(t,n)}),o={layoutInfo:function(){return a},map:function(t){return c=e.time(function(){var e=o.layoutInfo();d=t(e.positioning,e.rowCount)}),o},reflow:function(t){return l=e.time(function(){var e=t||r;e(d.grid,d.tiles)}),o},performance:function(){return{tileCount:n.length,layoutTime:s,mapTime:c,reflowTime:l,totalTime:s+c+l}}}}function o(e,t){e.element.css(e.style),t.forEach(function(e){e.element.css(e.style)})}function i(e,t){function n(t,n){if(t.col>e)throw"md-grid-list: Tile at position "+n+" has a colspan ("+t.col+") that exceeds the column count ("+e+")";for(var a=0,l=0;l-a<t.col;)d>=e?o():(a=c.indexOf(0,d),-1!==a&&-1!==(l=r(a+1))?d=l+1:(a=l=0,o()));return i(a,t.col,t.row),d=a+t.col,{col:a,row:s}}function o(){d=0,s++,i(0,e,-1)}function i(e,t,n){for(var o=e;e+t>o;o++)c[o]=Math.max(c[o]+n,0)}function r(e){var t;for(t=e;t<c.length;t++)if(0!==c[t])return t;return t===c.length?t:void 0}function a(){for(var t=[],n=0;e>n;n++)t.push(0);return t}var d=0,s=0,c=a();return{positioning:t.map(function(e,t){return{spans:e,position:n(e,t)}}),rowCount:s+Math.max.apply(Math,c)}}var r=o;return n.animateWith=function(e){r=t.isFunction(e)?e:o},n}function i(e){function n(n,o,i,r){o.attr("role","listitem");var a=e.watchResponsiveAttributes(["md-colspan","md-rowspan"],i,t.bind(r,r.invalidateLayout));r.invalidateTiles(),n.$on("$destroy",function(){o[0].$$mdDestroyed=!0,a(),r.invalidateLayout()}),t.isDefined(n.$parent.$index)&&n.$watch(function(){return n.$parent.$index},function(e,t){e!==t&&r.invalidateTiles()})}return{restrict:"E",require:"^mdGridList",template:"<figure ng-transclude></figure>",transclude:!0,scope:{},controller:["$attrs",function(e){this.$attrs=e}],link:n}}function r(){return{template:"<figcaption ng-transclude></figcaption>",transclude:!0}}t.module("material.components.gridList",["material.core"]).directive("mdGridList",e).directive("mdGridTile",i).directive("mdGridTileFooter",r).directive("mdGridTileHeader",r).factory("$mdGridLayout",o),e.$inject=["$interpolate","$mdConstant","$mdGridLayout","$mdMedia"],n.$inject=["$mdUtil"],n.prototype={invalidateTiles:function(){this.tilesInvalidated=!0,this.invalidateLayout()},invalidateLayout:function(){this.layoutInvalidated||(this.layoutInvalidated=!0,this.$timeout_(t.bind(this,this.layout)))},layout:function(){try{this.layoutDelegate(this.tilesInvalidated)}finally{this.layoutInvalidated=!1,this.tilesInvalidated=!1}}},o.$inject=["$mdUtil"],i.$inject=["$mdMedia"]}(),function(){function n(e,t){function n(t,n){e(n);var o=n[0].querySelector(r),i=n[0].querySelector(a);o&&n.addClass("md-icon-left"),i&&n.addClass("md-icon-right")}function o(e,n,o,i){var r=this;r.isErrorGetter=o.mdIsError&&t(o.mdIsError),r.delegateClick=function(){r.input.focus()},r.element=n,r.setFocused=function(e){n.toggleClass("md-input-focused",!!e)},r.setHasValue=function(e){n.toggleClass("md-input-has-value",!!e)},r.setHasPlaceholder=function(e){n.toggleClass("md-input-has-placeholder",!!e)},r.setInvalid=function(e){e?i.addClass(n,"md-input-invalid"):i.removeClass(n,"md-input-invalid")},e.$watch(function(){return r.label&&r.input},function(e){e&&!r.label.attr("for")&&r.label.attr("for",r.input.attr("id"))})}var i=["INPUT","TEXTAREA","SELECT","MD-SELECT"],r=i.reduce(function(e,t){return e.concat(["md-icon ~ "+t,".md-icon ~ "+t])},[]).join(","),a=i.reduce(function(e,t){return e.concat([t+" ~ md-icon",t+" ~ .md-icon"])},[]).join(",");return o.$inject=["$scope","$element","$attrs","$animate"],{restrict:"E",link:n,controller:o}}function o(){return{restrict:"E",require:"^?mdInputContainer",link:function(e,t,n,o){!o||n.mdNoFloat||t.hasClass("_md-container-ignore")||(o.label=t,e.$on("$destroy",function(){o.label=null}))}}}function i(e,n,o,i,r){function a(a,d,s,c){function l(e){return p.setHasValue(!g.$isEmpty(e)),e}function m(){p.label&&s.$observe("required",function(e){p.label.toggleClass("md-required",e&&!v)})}function u(){p.setHasValue(d.val().length>0||(d[0].validity||{}).badInput)}function h(){function o(){d.attr("rows",1).css("height","auto").addClass("md-no-flex");var e=c();if(E||(E=d.css("padding",0).prop("offsetHeight"),d.css("padding",null)),b&&E&&(e=Math.max(e,E*b)),v&&E){var t=E*v;e>t?(d.attr("md-no-autogrow",""),e=t):d.removeAttr("md-no-autogrow")}E&&d.attr("rows",Math.round(e/E)),d.css("height",e+"px").removeClass("md-no-flex")}function c(){var e=$.offsetHeight,t=$.scrollHeight-e;return e+Math.max(t,0)}function l(t){return e.nextTick(o),t}function m(){if(h&&(h=!1,t.element(n).off("resize",o),d.attr("md-no-autogrow","").off("input",o),f)){var e=g.$formatters.indexOf(l);e>-1&&g.$formatters.splice(e,1)}}function u(){function e(e){e.preventDefault(),l=!0,u=e.clientY,h=parseFloat(d.css("height"))||d.prop("offsetHeight")}function n(e){l&&(e.preventDefault(),m(),f.addClass("md-input-resized"))}function o(e){l&&d.css("height",h+(e.pointer.y-u)+"px")}function i(e){l&&(l=!1,f.removeClass("md-input-resized"))}if(!s.hasOwnProperty("mdNoResize")){var c=t.element('<div class="md-resize-handle"></div>'),l=!1,u=null,h=0,f=p.element,g=r.register(c,"drag",{ +horizontal:!1});d.after(c),c.on("mousedown",e),f.on("$md.dragstart",n).on("$md.drag",o).on("$md.dragend",i),a.$on("$destroy",function(){c.off("mousedown",e).remove(),f.off("$md.dragstart",n).off("$md.drag",o).off("$md.dragend",i),g(),c=null,f=null,g=null})}}var h=!s.hasOwnProperty("mdNoAutogrow");if(u(),h){var b=s.hasOwnProperty("rows")?parseInt(s.rows):NaN,v=s.hasOwnProperty("maxRows")?parseInt(s.maxRows):NaN,E=null,$=d[0];if(i(function(){e.nextTick(o)},10,!1),d.on("input",o),f&&g.$formatters.push(l),b||d.attr("rows",1),t.element(n).on("resize",o),a.$on("$destroy",m),s.hasOwnProperty("mdDetectHidden")){var C=function(){var e=!1;return function(){var t=0===$.offsetHeight;t===!1&&e===!0&&o(),e=t}}();a.$watch(function(){return e.nextTick(C,!1),!0})}}}var p=c[0],f=!!c[1],g=c[1]||e.fakeNgModel(),b=t.isDefined(s.readonly),v=e.parseAttributeBoolean(s.mdNoAsterisk),E=d[0].tagName.toLowerCase();if(p){if("hidden"===s.type)return void d.attr("aria-hidden","true");if(p.input)throw new Error("<md-input-container> can only have *one* <input>, <textarea> or <md-select> child element!");p.input=d,m();var $=t.element('<div class="md-errors-spacer">');d.after($),p.label||o.expect(d,"aria-label",d.attr("placeholder")),d.addClass("md-input"),d.attr("id")||d.attr("id","input_"+e.nextUid()),"input"===E&&"number"===s.type&&s.min&&s.max&&!s.step?d.attr("step","any"):"textarea"===E&&h(),f||u();var C=p.isErrorGetter||function(){return g.$invalid&&(g.$touched||y())},y=function(){var n=e.getClosest(d,"form"),o=n?t.element(n).controller("form"):null;return o?o.$submitted:!1};a.$watch(C,p.setInvalid),g.$parsers.push(l),g.$formatters.push(l),d.on("input",u),b||d.on("focus",function(t){e.nextTick(function(){p.setFocused(!0)})}).on("blur",function(t){e.nextTick(function(){p.setFocused(!1),u()})}),a.$on("$destroy",function(){p.setFocused(!1),p.setHasValue(!1),p.input=null})}}return{restrict:"E",require:["^?mdInputContainer","?ngModel"],link:a}}function r(e,n){function o(o,i,r,a){function d(e){return c.parent?(c.text(String(i.val()||e||"").length+"/"+s),e):e}var s,c,l,m=a[0],u=a[1];n.nextTick(function(){l=t.element(u.element[0].querySelector(".md-errors-spacer")),c=t.element('<div class="md-char-counter">'),l.append(c),r.$set("ngTrim","false"),m.$formatters.push(d),m.$viewChangeListeners.push(d),i.on("input keydown keyup",function(){d()}),o.$watch(r.mdMaxlength,function(n){s=n,t.isNumber(n)&&n>0?(c.parent().length||e.enter(c,l),d()):e.leave(c)}),m.$validators["md-maxlength"]=function(e,n){return!t.isNumber(s)||0>s?!0:(e||i.val()||n||"").length<=s}})}return{restrict:"A",require:["ngModel","^mdInputContainer"],link:o}}function a(e){function t(e,t,n,o){if(o){var i=o.element.find("label"),r=o.element.attr("md-no-float");if(i&&i.length||""===r||e.$eval(r))return void o.setHasPlaceholder(!0);var a=n.placeholder;if(t.removeAttr("placeholder"),o.input&&"MD-SELECT"!=o.input[0].nodeName){var d='<label ng-click="delegateClick()">'+a+"</label>";o.element.addClass("md-icon-float"),o.element.prepend(d)}}}return{restrict:"A",require:"^^?mdInputContainer",priority:200,link:t}}function d(e){function t(t,n,o){function i(){a=!0,e(function(){n[0].select(),a=!1},1,!1)}function r(e){a&&e.preventDefault()}if("INPUT"===n[0].nodeName||"TEXTAREA"===n[0].nodeName){var a=!1;n.on("focus",i).on("mouseup",r),t.$on("$destroy",function(){n.off("focus",i).off("mouseup",r)})}}return{restrict:"A",link:t}}function s(){function e(e,n,o,i){i&&(n.toggleClass("md-input-messages-animation",!0),n.toggleClass("md-auto-hide",!0),("false"==o.mdAutoHide||t(o))&&n.toggleClass("md-auto-hide",!1))}function t(e){return E.some(function(t){return e[t]})}return{restrict:"EA",link:e,require:"^^?mdInputContainer"}}function c(e){function t(t){function n(){for(var e=t[0];e=e.parentNode;)if(e.nodeType===Node.DOCUMENT_FRAGMENT_NODE)return!0;return!1}function o(t){return!!e.getClosest(t,"md-input-container")}function i(e){e.toggleClass("md-input-message-animation",!0)}if(o(t))i(t);else if(n())return function(e,n){o(n)&&i(t)}}return{restrict:"EA",compile:t,priority:100}}function l(e,t){return{addClass:function(n,o,i){var r=v(n);"md-input-invalid"==o&&r.hasClass("md-auto-hide")?h(n,t,e)["finally"](i):i()}}}function m(e,t){return{enter:function(n,o){h(n,t,e)["finally"](o)},leave:function(n,o){p(n,t,e)["finally"](o)},addClass:function(n,o,i){"ng-hide"==o?p(n,t,e)["finally"](i):i()},removeClass:function(n,o,i){"ng-hide"==o?h(n,t,e)["finally"](i):i()}}}function u(e){return{enter:function(t,n){var o=v(t);return o.hasClass("md-auto-hide")?void n():f(t,e)},leave:function(t,n){return g(t,e)}}}function h(e,n,o){var i,r=[],a=v(e);return t.forEach(a.children(),function(e){i=f(t.element(e),n),r.push(i.start())}),o.all(r)}function p(e,n,o){var i,r=[],a=v(e);return t.forEach(a.children(),function(e){i=g(t.element(e),n),r.push(i.start())}),o.all(r)}function f(e,t){var n=e[0].offsetHeight;return t(e,{event:"enter",structural:!0,from:{opacity:0,"margin-top":-n+"px"},to:{opacity:1,"margin-top":"0"},duration:.3})}function g(t,n){var o=t[0].offsetHeight,i=e.getComputedStyle(t[0]);return 0==i.opacity?n(t,{}):n(t,{event:"leave",structural:!0,from:{opacity:1,"margin-top":0},to:{opacity:0,"margin-top":-o+"px"},duration:.3})}function b(e){var t=e.controller("mdInputContainer");return t.element}function v(e){var n=b(e);return t.element(n[0].querySelector(".md-input-messages-animation"))}t.module("material.components.input",["material.core"]).directive("mdInputContainer",n).directive("label",o).directive("input",i).directive("textarea",i).directive("mdMaxlength",r).directive("placeholder",a).directive("ngMessages",s).directive("ngMessage",c).directive("ngMessageExp",c).directive("mdSelectOnFocus",d).animation(".md-input-invalid",l).animation(".md-input-messages-animation",m).animation(".md-input-message-animation",u),n.$inject=["$mdTheming","$parse"],i.$inject=["$mdUtil","$window","$mdAria","$timeout","$mdGesture"],r.$inject=["$animate","$mdUtil"],a.$inject=["$log"],d.$inject=["$timeout"];var E=["ngIf","ngShow","ngHide","ngSwitchWhen","ngSwitchDefault"];c.$inject=["$mdUtil"],l.$inject=["$q","$animateCss"],m.$inject=["$q","$animateCss"],u.$inject=["$animateCss"]}(),function(){function e(e){return{restrict:"E",compile:function(t){return t[0].setAttribute("role","list"),e}}}function n(e,n,o,i){var r=["md-checkbox","md-switch"];return{restrict:"E",controller:"MdListController",compile:function(a,d){function s(){for(var e,t,n=["md-switch","md-checkbox"],o=0;t=n[o];++o)if((e=a.find(t)[0])&&!e.hasAttribute("aria-label")){var i=a.find("p")[0];if(!i)return;e.setAttribute("aria-label","Toggle "+i.textContent)}}function c(e){if("div"==e)$=t.element('<div class="_md-no-style _md-list-item-inner">'),$.append(a.contents()),a.addClass("_md-proxy-focus");else{$=t.element('<div class="md-button _md-no-style"> <div class="_md-list-item-inner"></div></div>');var n=t.element('<md-button class="_md-no-style"></md-button>');n[0].setAttribute("aria-label",a[0].textContent),u(a[0],n[0]),$.prepend(n),$.children().eq(1).append(a.contents()),a.addClass("_md-button-wrap")}a[0].setAttribute("tabindex","-1"),a.append($)}function l(){var e=t.element('<div class="_md-secondary-container">');t.forEach(E,function(t){m(t,e)}),$.append(e)}function m(n,o){if(n&&!p(n)&&n.hasAttribute("ng-click")){e.expect(n,"aria-label");var i=t.element('<md-button class="md-secondary md-icon-button">');u(n,i[0]),n.setAttribute("tabindex","-1"),i.append(n),n=i[0]}n&&(!f(n)||!d.ngClick&&h(n))&&t.element(n).removeClass("md-secondary"),a.addClass("md-with-secondary"),o.append(n)}function u(e,n){var i=o.prefixer(["ng-if","ng-click","ng-dblclick","aria-label","ng-disabled","ui-sref","href","ng-href","target","ng-attr-ui-sref","ui-sref-opts"]);t.forEach(i,function(t){e.hasAttribute(t)&&(n.setAttribute(t,e.getAttribute(t)),e.removeAttribute(t))})}function h(e){return-1!=r.indexOf(e.nodeName.toLowerCase())}function p(e){var t=e.nodeName.toUpperCase();return"MD-BUTTON"==t||"BUTTON"==t}function f(e){for(var t=e.attributes,n=0;n<t.length;n++)if("ngClick"===d.$normalize(t[n].name))return!0;return!1}function g(e,a,d,s){function c(){u&&u.children&&!g&&t.forEach(r,function(e){t.forEach(u.querySelectorAll(e+":not(.md-secondary)"),function(e){m.push(e)})})}function l(){(1==m.length||g)&&(a.addClass("md-clickable"),g||s.attachRipple(e,t.element(a[0].querySelector("._md-no-style"))))}a.addClass("_md");var m=[],u=a[0].firstElementChild,h=a.hasClass("_md-button-wrap"),p=h?u.firstElementChild:u,g=p&&f(p);c(),l(),a.hasClass("_md-proxy-focus")&&m.length&&t.forEach(m,function(n){n=t.element(n),e.mouseActive=!1,n.on("mousedown",function(){e.mouseActive=!0,i(function(){e.mouseActive=!1},100)}).on("focus",function(){e.mouseActive===!1&&a.addClass("md-focused"),n.on("blur",function t(){a.removeClass("md-focused"),n.off("blur",t)})})});var b=function(e){if("INPUT"!=e.target.nodeName&&"TEXTAREA"!=e.target.nodeName&&!e.target.isContentEditable){var t=e.which||e.keyCode;t==n.KEY_CODE.SPACE&&p&&(p.click(),e.preventDefault(),e.stopPropagation())}};g||m.length||p&&p.addEventListener("keypress",b),a.off("click"),a.off("keypress"),1==m.length&&p&&a.children().eq(0).on("click",function(e){var n=o.getClosest(e.target,"BUTTON");!n&&p.contains(e.target)&&t.forEach(m,function(n){e.target===n||n.contains(e.target)||t.element(n).triggerHandler("click")})}),e.$on("$destroy",function(){p&&p.removeEventListener("keypress",b)})}var b,v,E=a[0].querySelectorAll(".md-secondary"),$=a;if(a[0].setAttribute("role","listitem"),d.ngClick||d.ngDblclick||d.ngHref||d.href||d.uiSref||d.ngAttrUiSref)c("button");else{for(var C,y=0;C=r[y];++y)if(v=a[0].querySelector(C)){b=!0;break}b?c("div"):a[0].querySelector("md-button:not(.md-secondary):not(.md-exclude)")||a.addClass("_md-no-proxy")}return l(),s(),g}}}function o(e,t,n){function o(e,t){var o={};n.attach(e,t,o)}var i=this;i.attachRipple=o}t.module("material.components.list",["material.core"]).controller("MdListController",o).directive("mdList",e).directive("mdListItem",n),e.$inject=["$mdTheming"],n.$inject=["$mdAria","$mdConstant","$mdUtil","$timeout"],o.$inject=["$scope","$element","$mdListInkRipple"]}(),function(){t.module("material.components.menu",["material.core","material.components.backdrop"])}(),function(){t.module("material.components.menuBar",["material.core","material.components.menu"])}(),function(){function e(e){return{restrict:"E",transclude:!0,controller:o,controllerAs:"ctrl",bindToController:!0,scope:{mdSelectedNavItem:"=?",navBarAriaLabel:"@?"},template:'<div class="md-nav-bar"><nav role="navigation"><ul class="_md-nav-bar-list" layout="row" ng-transclude role="listbox"tabindex="0"ng-focus="ctrl.onFocus()"ng-blur="ctrl.onBlur()"ng-keydown="ctrl.onKeydown($event)"aria-label="{{ctrl.navBarAriaLabel}}"></ul></nav><md-nav-ink-bar></md-nav-ink-bar></div>',link:function(n,o,i,r){r.navBarAriaLabel||e.expectAsync(o,"aria-label",t.noop)}}}function o(e,t,n,o){this._$timeout=n,this._$scope=t,this._$mdConstant=o,this.mdSelectedNavItem,this.navBarAriaLabel,this._navBarEl=e[0],this._inkbar;var i=this,r=this._$scope.$watch(function(){return i._navBarEl.querySelectorAll("._md-nav-button").length},function(e){e>0&&(i._initTabs(),r())})}function i(e){return{restrict:"E",require:["mdNavItem","^mdNavBar"],controller:r,bindToController:!0,controllerAs:"ctrl",replace:!0,transclude:!0,template:'<li class="md-nav-item" role="option" aria-selected="{{ctrl.isSelected()}}"><md-button ng-if="ctrl.mdNavSref" class="_md-nav-button md-accent"ng-class="ctrl.getNgClassMap()"tabindex="-1"ui-sref="{{ctrl.mdNavSref}}"><span ng-transclude class="_md-nav-button-text"></span></md-button><md-button ng-if="ctrl.mdNavHref" class="_md-nav-button md-accent"ng-class="ctrl.getNgClassMap()"tabindex="-1"ng-href="{{ctrl.mdNavHref}}"><span ng-transclude class="_md-nav-button-text"></span></md-button><md-button ng-if="ctrl.mdNavClick" class="_md-nav-button md-accent"ng-class="ctrl.getNgClassMap()"tabindex="-1"ng-click="ctrl.mdNavClick()"><span ng-transclude class="_md-nav-button-text"></span></md-button></li>',scope:{mdNavClick:"&?",mdNavHref:"@?",mdNavSref:"@?",name:"@"},link:function(n,o,i,r){var a=r[0],d=r[1];e(function(){a.name||(a.name=t.element(o[0].querySelector("._md-nav-button-text")).text().trim());var e=t.element(o[0].querySelector("._md-nav-button"));e.on("click",function(){d.mdSelectedNavItem=a.name,n.$apply()})})}}}function r(e){this._$element=e,this.mdNavClick,this.mdNavHref,this.name,this._selected=!1,this._focused=!1;var t=!!e.attr("md-nav-click"),n=!!e.attr("md-nav-href"),o=!!e.attr("md-nav-sref");if((t?1:0)+(n?1:0)+(o?1:0)>1)throw Error("Must specify exactly one of md-nav-click, md-nav-href, md-nav-sref for nav-item directive")}t.module("material.components.navBar",["material.core"]).controller("MdNavBarController",o).directive("mdNavBar",e).controller("MdNavItemController",r).directive("mdNavItem",i),e.$inject=["$mdAria"],o.$inject=["$element","$scope","$timeout","$mdConstant"],o.prototype._initTabs=function(){this._inkbar=t.element(this._navBarEl.getElementsByTagName("md-nav-ink-bar")[0]);var e=this;this._$timeout(function(){e._updateTabs(e.mdSelectedNavItem,n)}),this._$scope.$watch("ctrl.mdSelectedNavItem",function(t,n){e._$timeout(function(){e._updateTabs(t,n)})})},o.prototype._updateTabs=function(e,t){var n,o=this._getTabs();if(t){var i=this._getTabByName(t);i&&(i.setSelected(!1),n=o.indexOf(i))}if(e){var r=this._getTabByName(e);if(r){r.setSelected(!0);var a=o.indexOf(r),d=this;this._$timeout(function(){d._updateInkBarStyles(r,a,n)})}}},o.prototype._updateInkBarStyles=function(e,t,n){var o=e.getButtonEl(),i=o.offsetLeft;this._inkbar.toggleClass("_md-left",n>t).toggleClass("_md-right",t>n),this._inkbar.css({left:i+"px",width:o.offsetWidth+"px"})},o.prototype._getTabs=function(){var e=Array.prototype.slice.call(this._navBarEl.querySelectorAll(".md-nav-item"));return e.map(function(e){return t.element(e).controller("mdNavItem")})},o.prototype._getTabByName=function(e){return this._findTab(function(t){return t.getName()==e})},o.prototype._getSelectedTab=function(){return this._findTab(function(e){return e.isSelected()})},o.prototype.getFocusedTab=function(){return this._findTab(function(e){return e.hasFocus()})},o.prototype._findTab=function(e){for(var t=this._getTabs(),n=0;n<t.length;n++)if(e(t[n]))return t[n];return null},o.prototype.onFocus=function(){var e=this._getSelectedTab();e&&e.setFocused(!0)},o.prototype.onBlur=function(){var e=this.getFocusedTab();e&&e.setFocused(!1)},o.prototype._moveFocus=function(e,t){e.setFocused(!1),t.setFocused(!0)},o.prototype.onKeydown=function(e){var t=this._$mdConstant.KEY_CODE,n=this._getTabs(),o=this.getFocusedTab();if(o){var i=n.indexOf(o);switch(e.keyCode){case t.UP_ARROW:case t.LEFT_ARROW:i>0&&this._moveFocus(o,n[i-1]);break;case t.DOWN_ARROW:case t.RIGHT_ARROW:i<n.length-1&&this._moveFocus(o,n[i+1]);break;case t.SPACE:case t.ENTER:this._$timeout(function(){o.getButtonEl().click()})}}},i.$inject=["$$rAF"],r.$inject=["$element"],r.prototype.getNgClassMap=function(){return{"md-active":this._selected,"md-primary":this._selected,"md-unselected":!this._selected,"md-focused":this._focused}},r.prototype.getName=function(){return this.name},r.prototype.getButtonEl=function(){return this._$element[0].querySelector("._md-nav-button")},r.prototype.setSelected=function(e){this._selected=e},r.prototype.isSelected=function(){return this._selected},r.prototype.setFocused=function(e){this._focused=e},r.prototype.hasFocus=function(){return this._focused}}(),function(){function e(e,n,o,a){this._defaultConfigOptions={bindToController:!0,clickOutsideToClose:!1,disableParentScroll:!1,escapeToClose:!1,focusOnOpen:!0,fullscreen:!1,hasBackdrop:!1,transformTemplate:t.bind(this,this._wrapTemplate),trapFocus:!1,zIndex:d},this._config={},this._$rootElement=e,this._$rootScope=n,this._$injector=o,this._$window=a,this.animation=r.animation,this.xPosition=i.xPosition,this.yPosition=i.yPosition}function o(e,t){this._$q=t.get("$q"),this._$mdCompiler=t.get("$mdCompiler"),this._$mdConstant=t.get("$mdConstant"),this._$mdUtil=t.get("$mdUtil"),this._$rootScope=t.get("$rootScope"),this._$animate=t.get("$animate"),this._$mdPanel=t.get("$mdPanel"),this._$log=t.get("$log"),this._$window=t.get("$window"),this._$$rAF=t.get("$$rAF"),this.id=e.id,this.isAttached=!1,this._config=e,this._panelContainer,this._panelEl,this._removeListeners=[],this._topFocusTrap,this._bottomFocusTrap,this._backdropRef,this._restoreScroll=null}function i(e){this._$window=e,this._absolute=!1,this._relativeToEl,this._top="",this._bottom="",this._left="",this._right="",this._translateX=[],this._translateY=[],this._positions=[],this._actualPosition}function r(e){this._$mdUtil=e.get("$mdUtil"),this._openFrom,this._closeTo,this._animationClass=""}function a(e){var n=t.isString(e)?document.querySelector(e):e;return t.element(n)}t.module("material.components.panel",["material.core","material.components.backdrop"]).service("$mdPanel",e);var d=80,s="_md-panel-hidden",c=t.element('<div class="_md-panel-focus-trap" tabindex="0"></div>');e.$inject=["$rootElement","$rootScope","$injector","$window"],e.prototype.create=function(e){var n=e||{};this._config={scope:this._$rootScope.$new(!0),attachTo:this._$rootElement},t.extend(this._config,this._defaultConfigOptions,n);var i="panel_"+this._$injector.get("$mdUtil").nextUid(),r=t.extend({id:i},this._config);return new o(r,this._$injector)},e.prototype.open=function(e){var t=this.create(e);return t.open().then(function(){return t})},e.prototype.newPanelPosition=function(){return new i(this._$window)},e.prototype.newPanelAnimation=function(){return new r(this._$injector)},e.prototype._wrapTemplate=function(e){var t=e||"";return'<div class="md-panel-outer-wrapper"> <div class="md-panel" style="left: -9999px;">'+t+"</div></div>"},o.prototype.open=function(){var e=this;return this._$q(function(t,n){var o=e._done(t,e),i=e._simpleBind(e.show,e);e.attach().then(i).then(o)["catch"](n)})},o.prototype.close=function(){var e=this;return this._$q(function(t,n){var o=e._done(t,e),i=e._simpleBind(e.detach,e);e.hide().then(i).then(o)["catch"](n)})},o.prototype.attach=function(){if(this.isAttached&&this._panelEl)return this._$q.when(this);var e=this;return this._$q(function(n,o){var i=e._done(n,e),r=e._config.onDomAdded||t.noop,a=function(t){return e.isAttached=!0,e._addEventListeners(),t};e._$q.all([e._createBackdrop(),e._createPanel().then(a)["catch"](o)]).then(r).then(i)["catch"](o)})},o.prototype.detach=function(){if(!this.isAttached)return this._$q.when(this);var e=this,n=e._config.onDomRemoved||t.noop,o=function(){return e._removeEventListeners(),e._topFocusTrap&&e._topFocusTrap.parentNode&&e._topFocusTrap.parentNode.removeChild(e._topFocusTrap),e._bottomFocusTrap&&e._bottomFocusTrap.parentNode&&e._bottomFocusTrap.parentNode.removeChild(e._bottomFocusTrap),e._panelContainer.remove(),e.isAttached=!1,e._$q.when(e)};return this._restoreScroll&&(this._restoreScroll(),this._restoreScroll=null),this._$q(function(t,i){var r=e._done(t,e);e._$q.all([o(),e._backdropRef?e._backdropRef.detach():!0]).then(n).then(r)["catch"](i)})},o.prototype.destroy=function(){this._config.locals=null},o.prototype.show=function(){if(!this._panelContainer)return this._$q(function(e,t){t("Panel does not exist yet. Call open() or attach().")});if(!this._panelContainer.hasClass(s))return this._$q.when(this);var e=this,n=function(){return e.removeClass(s),e._animateOpen()};return this._$q(function(o,i){var r=e._done(o,e),a=e._config.onOpenComplete||t.noop;e._$q.all([e._backdropRef?e._backdropRef.show():e,n().then(function(){e._focusOnOpen()},i)]).then(a).then(r)["catch"](i)})},o.prototype.hide=function(){if(!this._panelContainer)return this._$q(function(e,t){t("Panel does not exist yet. Call open() or attach().")});if(this._panelContainer.hasClass(s))return this._$q.when(this);var e=this;return this._$q(function(n,o){var i=e._done(n,e),r=e._config.onRemoving||t.noop,d=function(){var t=e._config.origin;t&&a(t).focus()},c=function(){e.addClass(s)};e._$q.all([e._backdropRef?e._backdropRef.hide():e,e._animateClose().then(r).then(c).then(d)["catch"](o)]).then(i,o)})},o.prototype.addClass=function(e){if(!this._panelContainer)throw new Error("Panel does not exist yet. Call open() or attach().");this._panelContainer.hasClass(e)||this._panelContainer.addClass(e)},o.prototype.removeClass=function(e){if(!this._panelContainer)throw new Error("Panel does not exist yet. Call open() or attach().");this._panelContainer.hasClass(e)&&this._panelContainer.removeClass(e)},o.prototype.toggleClass=function(e){if(!this._panelContainer)throw new Error("Panel does not exist yet. Call open() or attach().");this._panelContainer.toggleClass(e)},o.prototype._createPanel=function(){var e=this;return this._$q(function(n,o){e._config.locals||(e._config.locals={}),e._config.locals.mdPanelRef=e,e._$mdCompiler.compile(e._config).then(function(i){e._panelContainer=i.link(e._config.scope),a(e._config.attachTo).append(e._panelContainer),e._config.disableParentScroll&&(e._restoreScroll=e._$mdUtil.disableScrollAround(null,e._panelContainer)),e._panelEl=t.element(e._panelContainer[0].querySelector(".md-panel")),e._config.panelClass&&e._panelEl.addClass(e._config.panelClass),e._$animate.pin&&e._$animate.pin(e._panelContainer,a(e._config.attachTo)),e._configureTrapFocus(),e._addStyles().then(function(){n(e)},o)},o)})},o.prototype._addStyles=function(){var e=this;return this._$q(function(t){e._panelContainer.css("z-index",e._config.zIndex),e._panelEl.css("z-index",e._config.zIndex+1);var n=function(){e._panelEl.css("left",""),e._panelContainer.addClass(s),t(e)};if(e._config.fullscreen)return e._panelEl.addClass("_md-panel-fullscreen"),void n();var o=e._config.position;return o?void e._$rootScope.$$postDigest(function(){e._updatePosition(!0),t(e)}):void n()})},o.prototype._updatePosition=function(e){var t=this._config.position;if(t){t._setPanelPosition(this._panelEl),e&&this._panelContainer.addClass(s),this._panelEl.css("top",t.getTop()),this._panelEl.css("bottom",t.getBottom()),this._panelEl.css("left",t.getLeft()),this._panelEl.css("right",t.getRight());var n=this._$mdConstant.CSS.TRANSFORM;this._panelEl.css(n,t.getTransform())}},o.prototype._focusOnOpen=function(){if(this._config.focusOnOpen){var e=this;this._$rootScope.$$postDigest(function(){var t=e._$mdUtil.findFocusTarget(e._panelEl)||e._panelEl;t.focus()})}},o.prototype._createBackdrop=function(){if(this._config.hasBackdrop){if(!this._backdropRef){var e=this._$mdPanel.newPanelAnimation().openFrom(this._config.attachTo).withAnimation({open:"_md-opaque-enter",close:"_md-opaque-leave"}),t={animation:e,attachTo:this._config.attachTo,focusOnOpen:!1,panelClass:"_md-panel-backdrop",zIndex:this._config.zIndex-1};this._backdropRef=this._$mdPanel.create(t)}if(!this._backdropRef.isAttached)return this._backdropRef.attach()}},o.prototype._addEventListeners=function(){this._configureEscapeToClose(),this._configureClickOutsideToClose(),this._configureScrollListener()},o.prototype._removeEventListeners=function(){this._removeListeners&&this._removeListeners.forEach(function(e){e()}),this._removeListeners=null},o.prototype._configureEscapeToClose=function(){if(this._config.escapeToClose){var e=a(this._config.attachTo),t=this,n=function(e){e.keyCode===t._$mdConstant.KEY_CODE.ESCAPE&&(e.stopPropagation(),e.preventDefault(),t.close())};this._panelContainer.on("keydown",n),e.on("keydown",n),this._removeListeners.push(function(){t._panelContainer.off("keydown",n),e.off("keydown",n)})}},o.prototype._configureClickOutsideToClose=function(){if(this._config.clickOutsideToClose){var e,t=this._panelContainer,n=function(t){e=t.target},o=this,i=function(n){e===t[0]&&n.target===t[0]&&(n.stopPropagation(),n.preventDefault(),o.close())};t.on("mousedown",n),t.on("mouseup",i),this._removeListeners.push(function(){t.off("mousedown",n),t.off("mouseup",i)})}},o.prototype._configureScrollListener=function(){var e=t.bind(this,this._updatePosition),n=this._$$rAF.throttle(e),o=this,i=function(){o._config.disableParentScroll||n()};this._$window.addEventListener("scroll",i,!0),this._removeListeners.push(function(){o._$window.removeEventListener("scroll",i,!0)})},o.prototype._configureTrapFocus=function(){if(this._panelEl.attr("tabIndex","-1"),this._config.trapFocus){var e=this._panelEl;this._topFocusTrap=c.clone()[0],this._bottomFocusTrap=c.clone()[0];var t=function(){e.focus()};this._topFocusTrap.addEventListener("focus",t),this._bottomFocusTrap.addEventListener("focus",t),this._removeListeners.push(this._simpleBind(function(){this._topFocusTrap.removeEventListener("focus",t),this._bottomFocusTrap.removeEventListener("focus",t)},this)),e[0].parentNode.insertBefore(this._topFocusTrap,e[0]),e.after(this._bottomFocusTrap)}},o.prototype._animateOpen=function(){this.addClass("md-panel-is-showing");var e=this._config.animation;if(!e)return this.addClass("_md-panel-shown"),this._$q.when(this);var t=this;return this._$q(function(n){var o=t._done(n,t),i=function(){t._$log.warn("MdPanel Animations failed. Showing panel without animating."),o()};e.animateOpen(t._panelEl).then(o,i)})},o.prototype._animateClose=function(){var e=this._config.animation;if(!e)return this.removeClass("md-panel-is-showing"),this.removeClass("_md-panel-shown"),this._$q.when(this);var t=this;return this._$q(function(n){var o=function(){t.removeClass("md-panel-is-showing"),n(t)},i=function(){t._$log.warn("MdPanel Animations failed. Hiding panel without animating."),o()};e.animateClose(t._panelEl).then(o,i)})},o.prototype._simpleBind=function(e,t){return function(n){return e.apply(t,n)}},o.prototype._done=function(e,t){return function(){e(t)}},i.xPosition={CENTER:"center",ALIGN_START:"align-start",ALIGN_END:"align-end",OFFSET_START:"offset-start",OFFSET_END:"offset-end"},i.yPosition={CENTER:"center",ALIGN_TOPS:"align-tops",ALIGN_BOTTOMS:"align-bottoms",ABOVE:"above",BELOW:"below"},i.prototype.absolute=function(){return this._absolute=!0,this},i.prototype.top=function(e){return this._bottom="",this._top=e||"0",this},i.prototype.bottom=function(e){return this._top="",this._bottom=e||"0",this},i.prototype.left=function(e){return this._right="",this._left=e||"0",this},i.prototype.right=function(e){return this._left="",this._right=e||"0",this},i.prototype.centerHorizontally=function(){return this._left="50%",this._right="",this._translateX=["-50%"],this},i.prototype.centerVertically=function(){return this._top="50%",this._bottom="",this._translateY=["-50%"],this},i.prototype.center=function(){return this.centerHorizontally().centerVertically()},i.prototype.relativeTo=function(e){return this._absolute=!1,this._relativeToEl=a(e),this},i.prototype.addPanelPosition=function(e,t){if(!this._relativeToEl)throw new Error("addPanelPosition can only be used with relative positioning. Set relativeTo first.");return this._validateXPosition(e),this._validateYPosition(t),this._positions.push({x:e,y:t}),this},i.prototype._validateYPosition=function(e){if(null!=e){for(var t,n=Object.keys(i.yPosition),o=[],r=0;t=n[r];r++){var a=i.yPosition[t];if(o.push(a),a===e)return}throw new Error("Panel y position only accepts the following values:\n"+o.join(" | "))}},i.prototype._validateXPosition=function(e){if(null!=e){for(var t,n=Object.keys(i.xPosition),o=[],r=0;t=n[r];r++){var a=i.xPosition[t];if(o.push(a),a===e)return}throw new Error("Panel x Position only accepts the following values:\n"+o.join(" | "))}},i.prototype.withOffsetX=function(e){return this._translateX.push(e),this},i.prototype.withOffsetY=function(e){return this._translateY.push(e),this},i.prototype.getTop=function(){return this._top},i.prototype.getBottom=function(){return this._bottom},i.prototype.getLeft=function(){return this._left},i.prototype.getRight=function(){return this._right},i.prototype.getTransform=function(){var e=this._reduceTranslateValues("translateX",this._translateX),t=this._reduceTranslateValues("translateY",this._translateY);return(e+" "+t).trim()},i.prototype._isOnscreen=function(e){var t=parseInt(this.getLeft()),n=parseInt(this.getTop()),o=t+e[0].offsetWidth,i=n+e[0].offsetHeight;return t>=0&&n>=0&&i<=this._$window.innerHeight&&o<=this._$window.innerWidth},i.prototype.getActualPosition=function(){return this._actualPosition},i.prototype._reduceTranslateValues=function(e,t){return t.map(function(t){return e+"("+t+")"}).join(" ")},i.prototype._setPanelPosition=function(e){if(!this._absolute){if(this._actualPosition)return void this._calculatePanelPosition(e,this._actualPosition);for(var t=0;t<this._positions.length&&(this._actualPosition=this._positions[t],this._calculatePanelPosition(e,this._actualPosition),!this._isOnscreen(e));t++);}},i.prototype._calculatePanelPosition=function(e,t){var n=e[0].getBoundingClientRect(),o=n.width,r=n.height,a=this._relativeToEl[0].getBoundingClientRect(),d=a.left,s=a.right,c=a.width;switch(t.x){case i.xPosition.OFFSET_START:this._left=d-o+"px";break;case i.xPosition.ALIGN_END:this._left=s-o+"px";break;case i.xPosition.CENTER:var l=d+.5*c-.5*o;this._left=l+"px";break;case i.xPosition.ALIGN_START:this._left=d+"px";break;case i.xPosition.OFFSET_END:this._left=s+"px"}var m=a.top,u=a.bottom,h=a.height;switch(t.y){case i.yPosition.ABOVE:this._top=m-r+"px";break;case i.yPosition.ALIGN_BOTTOMS:this._top=u-r+"px";break;case i.yPosition.CENTER:var p=m+.5*h-.5*r;this._top=p+"px";break;case i.yPosition.ALIGN_TOPS:this._top=m+"px";break;case i.yPosition.BELOW:this._top=u+"px"}},r.animation={SLIDE:"md-panel-animate-slide",SCALE:"md-panel-animate-scale",FADE:"md-panel-animate-fade"},r.prototype.openFrom=function(e){return e=e.target?e.target:e,this._openFrom=this._getPanelAnimationTarget(e),this._closeTo||(this._closeTo=this._openFrom),this},r.prototype.closeTo=function(e){return this._closeTo=this._getPanelAnimationTarget(e),this},r.prototype._getPanelAnimationTarget=function(e){return t.isDefined(e.top)||t.isDefined(e.left)?{element:n,bounds:{top:e.top||0,left:e.left||0}}:this._getBoundingClientRect(a(e))},r.prototype.withAnimation=function(e){return this._animationClass=e,this},r.prototype.animateOpen=function(e){var n=this._$mdUtil.dom.animator;this._fixBounds(e);var o={},i=e[0].style.transform||"",a=n.toTransformCss(i),d=n.toTransformCss(i);switch(this._animationClass){case r.animation.SLIDE:e.css("opacity","1"),o={transitionInClass:"_md-panel-animate-enter"};var s=n.calculateSlideToOrigin(e,this._openFrom)||"";a=n.toTransformCss(s+" "+i);break;case r.animation.SCALE:o={transitionInClass:"_md-panel-animate-enter"};var c=n.calculateZoomToOrigin(e,this._openFrom)||"";a=n.toTransformCss(c+" "+i);break;case r.animation.FADE:o={transitionInClass:"_md-panel-animate-enter"};break;default:o=t.isString(this._animationClass)?{transitionInClass:this._animationClass}:{transitionInClass:this._animationClass.open,transitionOutClass:this._animationClass.close}}return n.translate3d(e,a,d,o)},r.prototype.animateClose=function(e){var n=this._$mdUtil.dom.animator,o={},i=e[0].style.transform||"",a=n.toTransformCss(i),d=n.toTransformCss(i);switch(this._animationClass){case r.animation.SLIDE:e.css("opacity","1"),o={transitionInClass:"_md-panel-animate-leave"};var s=n.calculateSlideToOrigin(e,this._closeTo)||"";d=n.toTransformCss(s+" "+i);break;case r.animation.SCALE:o={transitionInClass:"_md-panel-animate-scale-out _md-panel-animate-leave"};var c=n.calculateZoomToOrigin(e,this._closeTo)||"";d=n.toTransformCss(c+" "+i);break;case r.animation.FADE:o={transitionInClass:"_md-panel-animate-fade-out _md-panel-animate-leave"};break;default:o=t.isString(this._animationClass)?{transitionOutClass:this._animationClass}:{transitionInClass:this._animationClass.close,transitionOutClass:this._animationClass.open}}return n.translate3d(e,a,d,o)},r.prototype._fixBounds=function(e){var t=e[0].offsetWidth,n=e[0].offsetHeight;this._openFrom&&null==this._openFrom.bounds.height&&(this._openFrom.bounds.height=n),this._openFrom&&null==this._openFrom.bounds.width&&(this._openFrom.bounds.width=t),this._closeTo&&null==this._closeTo.bounds.height&&(this._closeTo.bounds.height=n), +this._closeTo&&null==this._closeTo.bounds.width&&(this._closeTo.bounds.width=t)},r.prototype._getBoundingClientRect=function(e){return e instanceof t.element?{element:e,bounds:e[0].getBoundingClientRect()}:void 0}}(),function(){function e(e,n,o){function i(e,t,n){return e.attr("aria-valuemin",0),e.attr("aria-valuemax",100),e.attr("role","progressbar"),r}function r(o,i,r){function u(){r.$observe("value",function(e){var t=a(e);i.attr("aria-valuenow",t),p()!=l&&f($,t)}),r.$observe("mdBufferValue",function(e){f(E,a(e))}),r.$observe("disabled",function(e){b=e===!0||e===!1?e:t.isDefined(e),i.toggleClass(m,!!b)}),r.$observe("mdMode",function(e){switch(g&&C.removeClass(g),e){case l:case c:case d:case s:C.addClass(g="_md-mode-"+e);break;default:C.addClass(g="_md-mode-"+s)}})}function h(){if(t.isUndefined(r.mdMode)){var e=t.isDefined(r.value),n=e?d:s;i.attr("md-mode",n),r.mdMode=n}}function p(){var e=(r.mdMode||"").trim();if(e)switch(e){case d:case s:case c:case l:break;default:e=s}return e}function f(e,o){if(!b&&p()){var i=n.supplant("translateX({0}%) scale({1},1)",[(o-100)/2,o/100]),r=v({transform:i});t.element(e).css(r)}}e(i);var g,b=r.hasOwnProperty("disabled"),v=n.dom.animator.toCss,E=t.element(i[0].querySelector("._md-bar1")),$=t.element(i[0].querySelector("._md-bar2")),C=t.element(i[0].querySelector("._md-container"));i.attr("md-mode",p()).toggleClass(m,b),h(),u()}function a(e){return Math.max(0,Math.min(e||0,100))}var d="determinate",s="indeterminate",c="buffer",l="query",m="_md-progress-linear-disabled";return{restrict:"E",template:'<div class="_md-container"><div class="_md-dashed"></div><div class="_md-bar _md-bar1"></div><div class="_md-bar _md-bar2"></div></div>',compile:i}}t.module("material.components.progressLinear",["material.core"]).directive("mdProgressLinear",e),e.$inject=["$mdTheming","$mdUtil","$log"]}(),function(){t.module("material.components.progressCircular",["material.core"])}(),function(){function e(e,n,o,i){function r(r,a,d,s){function c(){a.hasClass("md-focused")||a.addClass("md-focused")}function l(o){var i=o.which||o.keyCode;if(i==n.KEY_CODE.ENTER||o.currentTarget==o.target)switch(i){case n.KEY_CODE.LEFT_ARROW:case n.KEY_CODE.UP_ARROW:o.preventDefault(),m.selectPrevious(),c();break;case n.KEY_CODE.RIGHT_ARROW:case n.KEY_CODE.DOWN_ARROW:o.preventDefault(),m.selectNext(),c();break;case n.KEY_CODE.ENTER:var r=t.element(e.getClosest(a[0],"form"));r.length>0&&r.triggerHandler("submit")}}a.addClass("_md"),o(a);var m=s[0],u=s[1]||e.fakeNgModel();m.init(u),r.mouseActive=!1,a.attr({role:"radiogroup",tabIndex:a.attr("tabindex")||"0"}).on("keydown",l).on("mousedown",function(e){r.mouseActive=!0,i(function(){r.mouseActive=!1},100)}).on("focus",function(){r.mouseActive===!1&&m.$element.addClass("md-focused")}).on("blur",function(){m.$element.removeClass("md-focused")})}function a(e){this._radioButtonRenderFns=[],this.$element=e}function d(){return{init:function(e){this._ngModelCtrl=e,this._ngModelCtrl.$render=t.bind(this,this.render)},add:function(e){this._radioButtonRenderFns.push(e)},remove:function(e){var t=this._radioButtonRenderFns.indexOf(e);-1!==t&&this._radioButtonRenderFns.splice(t,1)},render:function(){this._radioButtonRenderFns.forEach(function(e){e()})},setViewValue:function(e,t){this._ngModelCtrl.$setViewValue(e,t),this.render()},getViewValue:function(){return this._ngModelCtrl.$viewValue},selectNext:function(){return s(this.$element,1)},selectPrevious:function(){return s(this.$element,-1)},setActiveDescendant:function(e){this.$element.attr("aria-activedescendant",e)}}}function s(n,o){var i=e.iterator(n[0].querySelectorAll("md-radio-button"),!0);if(i.count()){var r=function(e){return!t.element(e).attr("disabled")},a=n[0].querySelector("md-radio-button.md-checked"),d=i[0>o?"previous":"next"](a,r)||i.first();t.element(d).triggerHandler("click")}}return a.prototype=d(),{restrict:"E",controller:["$element",a],require:["mdRadioGroup","?ngModel"],link:{pre:r}}}function n(e,t,n){function o(o,r,a,d){function s(e){if(!d)throw"RadioGroupController not found.";d.add(l),a.$observe("value",l),r.on("click",c).on("$destroy",function(){d.remove(l)})}function c(e){r[0].hasAttribute("disabled")||o.$apply(function(){d.setViewValue(a.value,e&&e.type)})}function l(){function e(e){"MD-RADIO-GROUP"!=r.parent()[0].nodeName&&r.parent()[e?"addClass":"removeClass"](i)}var t=d.getViewValue()==a.value;t!==u&&(u=t,r.attr("aria-checked",t),t?(e(!0),r.addClass(i),d.setActiveDescendant(r.attr("id"))):(e(!1),r.removeClass(i)))}function m(n,o){function i(){return a.id||"radio_"+t.nextUid()}o.ariaId=i(),n.attr({id:o.ariaId,role:"radio","aria-checked":"false"}),e.expectWithText(n,"aria-label")}var u;n(r),m(r,o),s()}var i="md-checked";return{restrict:"E",require:"^mdRadioGroup",transclude:!0,template:'<div class="_md-container" md-ink-ripple md-ink-ripple-checkbox><div class="_md-off"></div><div class="_md-on"></div></div><div ng-transclude class="_md-label"></div>',link:o}}t.module("material.components.radioButton",["material.core"]).directive("mdRadioGroup",e).directive("mdRadioButton",n),e.$inject=["$mdUtil","$mdConstant","$mdTheming","$timeout"],n.$inject=["$mdAria","$mdUtil","$mdTheming"]}(),function(){function e(e,o,i,r,a,d){function s(a,s){var c=t.element("<md-select-value><span></span></md-select-value>");if(c.append('<span class="_md-select-icon" aria-hidden="true"></span>'),c.addClass("_md-select-value"),c[0].hasAttribute("id")||c.attr("id","select_value_label_"+o.nextUid()),a.find("md-content").length||a.append(t.element("<md-content>").append(a.contents())),s.mdOnOpen&&(a.find("md-content").prepend(t.element('<div> <md-progress-circular md-mode="indeterminate" ng-if="$$loadingAsyncDone === false" md-diameter="25px"></md-progress-circular></div>')),a.find("md-option").attr("ng-show","$$loadingAsyncDone")),s.name){var l=t.element('<select class="_md-visually-hidden">');l.attr({name:"."+s.name,"ng-model":s.ngModel,"aria-hidden":"true",tabindex:"-1"});var m=a.find("md-option");t.forEach(m,function(e){var n=t.element("<option>"+e.innerHTML+"</option>");e.hasAttribute("ng-value")?n.attr("ng-value",e.getAttribute("ng-value")):e.hasAttribute("value")&&n.attr("value",e.getAttribute("value")),l.append(n)}),a.parent().append(l)}var u=o.parseAttributeBoolean(s.multiple),h=u?"multiple":"",p='<div class="_md-select-menu-container" aria-hidden="true"><md-select-menu {0}>{1}</md-select-menu></div>';return p=o.supplant(p,[h,a.html()]),a.empty().append(c),a.append(p),s.tabindex||s.$set("tabindex",0),function(a,s,c,l){function m(){var e=s.attr("aria-label")||s.attr("placeholder");!e&&y&&y.label&&(e=y.label.text()),$=e,r.expect(s,"aria-label",e)}function h(){N&&(D=D||N.find("md-select-menu").controller("mdSelectMenu"),M.setLabelText(D.selectedLabels()))}function p(){if($){var e=D.selectedLabels({mode:"aria"});s.attr("aria-label",e.length?$+": "+e:$)}}function f(){y&&y.setHasValue(D.selectedLabels().length>0||(s[0].validity||{}).badInput)}function g(){if(N=t.element(s[0].querySelector("._md-select-menu-container")),S=a,c.mdContainerClass){var e=N[0].getAttribute("class")+" "+c.mdContainerClass;N[0].setAttribute("class",e)}D=N.find("md-select-menu").controller("mdSelectMenu"),D.init(_,c.ngModel),s.on("$destroy",function(){N.remove()})}function b(e){var n=[32,13,38,40];if(-1!=n.indexOf(e.keyCode))e.preventDefault(),v(e);else if(e.keyCode<=90&&e.keyCode>=31){e.preventDefault();var o=D.optNodeForKeyboardSearch(e);if(!o)return;var i=t.element(o).controller("mdOption");D.isMultiple||D.deselect(Object.keys(D.selected)[0]),D.select(i.hashKey,i.value),D.refreshViewValue()}}function v(){S.isOpen=!0,s.attr("aria-expanded","true"),e.show({scope:S,preserveScope:!0,skipCompile:!0,element:N,target:s[0],selectCtrl:M,preserveElement:!0,hasBackdrop:!0,loadingAsync:c.mdOnOpen?a.$eval(c.mdOnOpen)||!0:!1})["finally"](function(){S.isOpen=!1,s.focus(),s.attr("aria-expanded","false"),_.$setTouched()})}var E,$,C=!0,y=l[0],M=l[1],_=l[2],A=l[3],T=s.find("md-select-value"),w=t.isDefined(c.readonly),k=o.parseAttributeBoolean(c.mdNoAsterisk);if(y){var x=y.isErrorGetter||function(){return _.$invalid&&_.$touched};if(y.input&&s.find("md-select-header").find("input")[0]!==y.input[0])throw new Error("<md-input-container> can only have *one* child <input>, <textarea> or <select> element!");y.input=s,y.label||r.expect(s,"aria-label",s.attr("placeholder")),a.$watch(x,y.setInvalid)}var N,S,D;if(g(),i(s),c.name&&A){var H=s.parent()[0].querySelector('select[name=".'+c.name+'"]');o.nextTick(function(){var e=t.element(H).controller("ngModel");e&&A.$removeControl(e)})}A&&t.isDefined(c.multiple)&&o.nextTick(function(){var e=_.$modelValue||_.$viewValue;e&&A.$setPristine()});var I=_.$render;_.$render=function(){I(),h(),p(),f()},c.$observe("placeholder",_.$render),y&&y.label&&c.$observe("required",function(e){y.label.toggleClass("md-required",e&&!k)}),M.setLabelText=function(e){if(M.setIsPlaceholder(!e),c.mdSelectedText)e=d(c.mdSelectedText)(a);else{var t=c.placeholder||(y&&y.label?y.label.text():"");e=e||t||""}var n=T.children().eq(0);n.html(e)},M.setIsPlaceholder=function(e){e?(T.addClass("_md-select-placeholder"),y&&y.label&&y.label.addClass("_md-placeholder")):(T.removeClass("_md-select-placeholder"),y&&y.label&&y.label.removeClass("_md-placeholder"))},w||(s.on("focus",function(e){y&&y.element.hasClass("md-input-has-value")&&y.setFocused(!0)}),s.on("blur",function(e){C&&(C=!1,S.isOpen&&e.stopImmediatePropagation()),S.isOpen||(y&&y.setFocused(!1),f())})),M.triggerClose=function(){d(c.mdOnClose)(a)},a.$$postDigest(function(){m(),h(),p()}),a.$watch(function(){return D.selectedLabels()},h);var O;c.$observe("ngMultiple",function(e){O&&O();var t=d(e);O=a.$watch(function(){return t(a)},function(e,t){e===n&&t===n||(e?s.attr("multiple","multiple"):s.removeAttr("multiple"),s.attr("aria-multiselectable",e?"true":"false"),N&&(D.setMultiple(e),I=_.$render,_.$render=function(){I(),h(),p(),f()},_.$render()))})}),c.$observe("disabled",function(e){t.isString(e)&&(e=!0),E!==n&&E===e||(E=e,e?s.attr({"aria-disabled":"true"}).removeAttr("tabindex").off("click",v).off("keydown",b):s.attr({tabindex:c.tabindex,"aria-disabled":"false"}).on("click",v).on("keydown",b))}),c.hasOwnProperty("disabled")||c.hasOwnProperty("ngDisabled")||(s.attr({"aria-disabled":"false"}),s.on("click",v),s.on("keydown",b));var R={role:"listbox","aria-expanded":"false","aria-multiselectable":u&&!c.ngMultiple?"true":"false"};s[0].hasAttribute("id")||(R.id="select_"+o.nextUid());var L="select_container_"+o.nextUid();N.attr("id",L),R["aria-owns"]=L,s.attr(R),a.$on("$destroy",function(){e.destroy()["finally"](function(){y&&(y.setFocused(!1),y.setHasValue(!1),y.input=null),_.$setTouched()})})}}return{restrict:"E",require:["^?mdInputContainer","mdSelect","ngModel","?^form"],compile:s,controller:function(){}}}function o(e,o,i){function r(e,n,r,a){function d(e){13!=e.keyCode&&32!=e.keyCode||s(e)}function s(n){var i=o.getClosest(n.target,"md-option"),r=i&&t.element(i).data("$mdOptionController");if(i&&r){if(i.hasAttribute("disabled"))return n.stopImmediatePropagation(),!1;var a=c.hashGetter(r.value),d=t.isDefined(c.selected[a]);e.$apply(function(){c.isMultiple?d?c.deselect(a):c.select(a,r.value):d||(c.deselect(Object.keys(c.selected)[0]),c.select(a,r.value)),c.refreshViewValue()})}}var c=a[0];n.addClass("_md"),i(n),n.on("click",s),n.on("keypress",d)}function a(i,r,a){function d(){var e=l.ngModel.$modelValue||l.ngModel.$viewValue||[];if(t.isArray(e)){var n=Object.keys(l.selected),o=e.map(l.hashGetter),i=n.filter(function(e){return-1===o.indexOf(e)});i.forEach(l.deselect),o.forEach(function(t,n){l.select(t,e[n])})}}function s(){var e=l.ngModel.$viewValue||l.ngModel.$modelValue;Object.keys(l.selected).forEach(l.deselect),l.select(l.hashGetter(e),e)}var l=this;l.isMultiple=t.isDefined(r.multiple),l.selected={},l.options={},i.$watchCollection(function(){return l.options},function(){l.ngModel.$render()});var m,u;l.setMultiple=function(e){function n(e,n){return t.isArray(e||n||[])}var o=l.ngModel;u=u||o.$isEmpty,l.isMultiple=e,m&&m(),l.isMultiple?(o.$validators["md-multiple"]=n,o.$render=d,i.$watchCollection(l.modelBinding,function(e){n(e)&&d(e),l.ngModel.$setPristine()}),o.$isEmpty=function(e){return!e||0===e.length}):(delete o.$validators["md-multiple"],o.$render=s)};var h,p,f,g="",b=300;l.optNodeForKeyboardSearch=function(e){h&&clearTimeout(h),h=setTimeout(function(){h=n,g="",f=n,p=n},b),g+=String.fromCharCode(e.keyCode);var o=new RegExp("^"+g,"i");p||(p=a.find("md-option"),f=new Array(p.length),t.forEach(p,function(e,t){f[t]=e.textContent.trim()}));for(var i=0;i<f.length;++i)if(o.test(f[i]))return p[i]},l.init=function(n,o){if(l.ngModel=n,l.modelBinding=o,l.ngModel.$isEmpty=function(e){return!l.options[e]},n.$options&&n.$options.trackBy){var r={},a=e(n.$options.trackBy);l.hashGetter=function(e,t){return r.$value=e,a(t||i,r)}}else l.hashGetter=function(e){return t.isObject(e)?"object_"+(e.$$mdSelectId||(e.$$mdSelectId=++c)):e};l.setMultiple(l.isMultiple)},l.selectedLabels=function(e){e=e||{};var t=e.mode||"html",n=o.nodesToArray(a[0].querySelectorAll("md-option[selected]"));if(n.length){var i;return"html"==t?i=function(e){var t=e.innerHTML,n=e.querySelector(".md-ripple-container");return n?t.replace(n.outerHTML,""):t}:"aria"==t&&(i=function(e){return e.hasAttribute("aria-label")?e.getAttribute("aria-label"):e.textContent}),n.map(i).join(", ")}return""},l.select=function(e,t){var n=l.options[e];n&&n.setSelected(!0),l.selected[e]=t},l.deselect=function(e){var t=l.options[e];t&&t.setSelected(!1),delete l.selected[e]},l.addOption=function(e,n){if(t.isDefined(l.options[e]))throw new Error('Duplicate md-option values are not allowed in a select. Duplicate value "'+n.value+'" found.');l.options[e]=n,t.isDefined(l.selected[e])&&(l.select(e,n.value),l.refreshViewValue())},l.removeOption=function(e){delete l.options[e]},l.refreshViewValue=function(){var e,n=[];for(var o in l.selected)(e=l.options[o])?n.push(e.value):n.push(l.selected[o]);var i=l.ngModel.$options&&l.ngModel.$options.trackBy,r=l.isMultiple?n:n[0],a=l.ngModel.$modelValue;(i?t.equals(a,r):a==r)||(l.ngModel.$setViewValue(r),l.ngModel.$render())}}return a.$inject=["$scope","$attrs","$element"],{restrict:"E",require:["mdSelectMenu"],scope:!1,controller:a,link:{pre:r}}}function i(e,n){function o(e,n){return e.append(t.element('<div class="_md-text">').append(e.contents())),e.attr("tabindex",n.tabindex||"0"),i}function i(o,i,r,a){function d(e,t,n){if(!m.hashGetter)return void(n||o.$$postDigest(function(){d(e,t,!0)}));var i=m.hashGetter(t,o),r=m.hashGetter(e,o);c.hashKey=r,c.value=e,m.removeOption(i,c),m.addOption(r,c)}function s(){var e={role:"option","aria-selected":"false"};i[0].hasAttribute("id")||(e.id="select_option_"+n.nextUid()),i.attr(e)}var c=a[0],m=a[1];m.isMultiple&&(i.addClass("_md-checkbox-enabled"),i.prepend(l.clone())),t.isDefined(r.ngValue)?o.$watch(r.ngValue,d):t.isDefined(r.value)?d(r.value):o.$watch(function(){return i.text().trim()},d),r.$observe("disabled",function(e){e?i.attr("tabindex","-1"):i.attr("tabindex","0")}),o.$$postDigest(function(){r.$observe("selected",function(e){t.isDefined(e)&&("string"==typeof e&&(e=!0),e?(m.isMultiple||m.deselect(Object.keys(m.selected)[0]),m.select(c.hashKey,c.value)):m.deselect(c.hashKey),m.refreshViewValue())})}),e.attach(o,i),s(),o.$on("$destroy",function(){m.removeOption(c.hashKey,c)})}function r(e){this.selected=!1,this.setSelected=function(t){t&&!this.selected?e.attr({selected:"selected","aria-selected":"true"}):!t&&this.selected&&(e.removeAttr("selected"),e.attr("aria-selected","false")),this.selected=t}}return r.$inject=["$element"],{restrict:"E",require:["mdOption","^^mdSelectMenu"],controller:r,compile:o}}function r(){function e(e,n){function o(){return e.parent().find("md-select-header").length}function i(){var o=e.find("label");o.length||(o=t.element("<label>"),e.prepend(o)),o.addClass("_md-container-ignore"),n.label&&o.text(n.label)}o()||i()}return{restrict:"E",compile:e}}function a(){return{restrict:"E"}}function d(e){function o(e,o,c,l,m,u,h,p,f){function g(e,t,n){function o(){return h(t,{addClass:"_md-leave"}).start()}function i(){t.removeClass("_md-active"),t.attr("aria-hidden","true"),t[0].style.display="none",v(n),!n.$destroy&&n.restoreFocus&&n.target.focus()}return n=n||{},n.cleanupInteraction(),n.cleanupResizing(),n.hideBackdrop(),n.$destroy===!0?i():o().then(i)}function b(i,r,a){function d(e,t,n){return n.parent.append(t),m(function(e,n){try{h(t,{removeClass:"_md-leave",duration:0}).start().then(s).then(e)}catch(o){n(o)}})}function s(){return m(function(e){if(a.isRemoved)return m.reject(!1);var t=E(i,r,a);t.container.element.css(C.toCss(t.container.styles)),t.dropDown.element.css(C.toCss(t.dropDown.styles)),u(function(){r.addClass("_md-active"),t.dropDown.element.css(C.toCss({transform:""})),b(a.focusedNode),e()})})}function g(e,t,n){return n.disableParentScroll&&!c.getClosest(n.target,"MD-DIALOG")?n.restoreScroll=c.disableScrollAround(n.element,n.parent):n.disableParentScroll=!1,n.hasBackdrop&&(n.backdrop=c.createBackdrop(e,"_md-select-backdrop _md-click-catcher"),p.enter(n.backdrop,f[0].body,null,{duration:0})),function(){n.backdrop&&n.backdrop.remove(),n.disableParentScroll&&n.restoreScroll(),delete n.restoreScroll}}function b(e){e&&!e.hasAttribute("disabled")&&e.focus()}function v(e,n){var o=r.find("md-select-menu");if(!n.target)throw new Error(c.supplant($,[n.target]));t.extend(n,{isRemoved:!1,target:t.element(n.target),parent:t.element(n.parent),selectEl:o,contentEl:r.find("md-content"),optionNodes:o[0].getElementsByTagName("md-option")})}function y(){var e=function(e,t,n){return function(){if(!n.isRemoved){var o=E(e,t,n),i=o.container,r=o.dropDown;i.element.css(C.toCss(i.styles)),r.element.css(C.toCss(r.styles))}}}(i,r,a),n=t.element(l);return n.on("resize",e),n.on("orientationchange",e),function(){n.off("resize",e),n.off("orientationchange",e)}}function M(){a.loadingAsync&&!a.isRemoved&&(i.$$loadingAsyncDone=!1,m.when(a.loadingAsync).then(function(){i.$$loadingAsyncDone=!0,delete a.loadingAsync}).then(function(){u(s)}))}function _(){function t(t){t.preventDefault(),t.stopPropagation(),a.restoreFocus=!1,c.nextTick(e.hide,!0)}function i(t){var n=o.KEY_CODE;switch(t.preventDefault(),t.stopPropagation(),t.keyCode){case n.UP_ARROW:return l();case n.DOWN_ARROW:return s();case n.SPACE:case n.ENTER:var i=c.getClosest(t.target,"md-option");i&&(u.triggerHandler({type:"click",target:i}),t.preventDefault()),m(t);break;case n.TAB:case n.ESCAPE:t.stopPropagation(),t.preventDefault(),a.restoreFocus=!0,c.nextTick(e.hide,!0);break;default:if(t.keyCode>=31&&t.keyCode<=90){var r=u.controller("mdSelectMenu").optNodeForKeyboardSearch(t);a.focusedNode=r||a.focusedNode,r&&r.focus()}}}function d(e){var t,o=c.nodesToArray(a.optionNodes),i=o.indexOf(a.focusedNode);do-1===i?i=0:"next"===e&&i<o.length-1?i++:"prev"===e&&i>0&&i--,t=o[i],t.hasAttribute("disabled")&&(t=n);while(!t&&i<o.length-1&&i>0);t&&t.focus(),a.focusedNode=t}function s(){d("next")}function l(){d("prev")}function m(t){function n(){var e=!1;if(t&&t.currentTarget.children.length>0){var n=t.currentTarget.children[0],o=n.scrollHeight>n.clientHeight;if(o&&n.children.length>0){var i=t.pageX-t.currentTarget.getBoundingClientRect().left;i>n.querySelector("md-option").offsetWidth&&(e=!0)}}return e}if(!(t&&"click"==t.type&&t.currentTarget!=u[0]||n())){var o=c.getClosest(t.target,"md-option");o&&o.hasAttribute&&!o.hasAttribute("disabled")&&(t.preventDefault(),t.stopPropagation(),h.isMultiple||(a.restoreFocus=!0,c.nextTick(function(){e.hide(h.ngModel.$viewValue)},!0)))}}if(!a.isRemoved){var u=a.selectEl,h=u.controller("mdSelectMenu")||{};return r.addClass("_md-clickable"),a.backdrop&&a.backdrop.on("click",t),u.on("keydown",i),u.on("click",m),function(){a.backdrop&&a.backdrop.off("click",t),u.off("keydown",i),u.off("click",m),r.removeClass("_md-clickable"),a.isRemoved=!0}}}return M(),v(i,a),a.hideBackdrop=g(i,r,a),d(i,r,a).then(function(e){return r.attr("aria-hidden","false"),a.alreadyOpen=!0,a.cleanupInteraction=_(),a.cleanupResizing=y(),e},a.hideBackdrop)}function v(e){var t=e.selectCtrl;if(t){var n=e.selectEl.controller("mdSelectMenu");t.setLabelText(n?n.selectedLabels():""),t.triggerClose()}}function E(e,n,o){var m,u=n[0],h=o.target[0].children[0],p=f[0].body,g=o.selectEl[0],b=o.contentEl[0],v=p.getBoundingClientRect(),E=h.getBoundingClientRect(),$=!1,C={left:v.left+s,top:s,bottom:v.height-s,right:v.width-s-(c.floatingScrollbars()?16:0)},y={top:E.top-C.top,left:E.left-C.left,right:C.right-(E.left+E.width),bottom:C.bottom-(E.top+E.height)},M=v.width-2*s,_=g.querySelector("md-option[selected]"),A=g.getElementsByTagName("md-option"),T=g.getElementsByTagName("md-optgroup"),w=d(n,b),k=i(o.loadingAsync);m=k?b.firstElementChild||b:_?_:T.length?T[0]:A.length?A[0]:b.firstElementChild||b,b.offsetWidth>M?b.style["max-width"]=M+"px":b.style.maxWidth=null,$&&(b.style["min-width"]=E.width+"px"),w&&g.classList.add("_md-overflow");var x=m;"MD-OPTGROUP"===(x.tagName||"").toUpperCase()&&(x=A[0]||b.firstElementChild||b,m=x),o.focusedNode=x,u.style.display="block";var N=g.getBoundingClientRect(),S=a(m);if(m){var D=l.getComputedStyle(m);S.paddingLeft=parseInt(D.paddingLeft,10)||0,S.paddingRight=parseInt(D.paddingRight,10)||0}if(w){var H=b.offsetHeight/2;b.scrollTop=S.top+S.height/2-H,y.top<H?b.scrollTop=Math.min(S.top,b.scrollTop+H-y.top):y.bottom<H&&(b.scrollTop=Math.max(S.top+S.height-N.height,b.scrollTop-H+y.bottom))}var I,O,R,L;$?(I=E.left,O=E.top+E.height,R="50% 0",O+N.height>C.bottom&&(O=E.top-N.height,R="50% 100%")):(I=E.left+S.left-S.paddingLeft+2,O=Math.floor(E.top+E.height/2-S.height/2-S.top+b.scrollTop)+2,R=S.left+E.width/2+"px "+(S.top+S.height/2-b.scrollTop)+"px 0px",L=Math.min(E.width+S.paddingLeft+S.paddingRight,M));var P=u.getBoundingClientRect(),F=Math.round(100*Math.min(E.width/N.width,1))/100,B=Math.round(100*Math.min(E.height/N.height,1))/100;return{container:{element:t.element(u),styles:{left:Math.floor(r(C.left,I,C.right-P.width)),top:Math.floor(r(C.top,O,C.bottom-P.height)),"min-width":L}},dropDown:{element:t.element(g),styles:{transformOrigin:R,transform:o.alreadyOpen?"":c.supplant("scale({0},{1})",[F,B])}}}}var $="$mdSelect.show() expected a target element in options.target but got '{0}'!",C=c.dom.animator;return{parent:"body",themable:!0,onShow:b,onRemove:g,hasBackdrop:!0,disableParentScroll:!0}}function i(e){return e&&t.isFunction(e.then)}function r(e,t,n){return Math.max(e,Math.min(t,n))}function a(e){return e?{left:e.offsetLeft,top:e.offsetTop,width:e.offsetWidth,height:e.offsetHeight}:{left:0,top:0,width:0,height:0}}function d(e,t){var n=!1;try{var o=e[0].style.display;e[0].style.display="block",n=t.scrollHeight>t.offsetHeight,e[0].style.display=o}finally{}return n}return o.$inject=["$mdSelect","$mdConstant","$mdUtil","$window","$q","$$rAF","$animateCss","$animate","$document"],e("$mdSelect").setDefaults({methods:["target"],options:o})}var s=8,c=0,l=t.element('<div class="_md-container"><div class="_md-icon"></div></div>');t.module("material.components.select",["material.core","material.components.backdrop"]).directive("mdSelect",e).directive("mdSelectMenu",o).directive("mdOption",i).directive("mdOptgroup",r).directive("mdSelectHeader",a).provider("$mdSelect",d),e.$inject=["$mdSelect","$mdUtil","$mdTheming","$mdAria","$compile","$parse"],o.$inject=["$parse","$mdUtil","$mdTheming"],i.$inject=["$mdButtonInkRipple","$mdUtil"],d.$inject=["$$interimElementProvider"]}(),function(){function n(t,n){return["$mdUtil",function(o){return{restrict:"A",multiElement:!0,link:function(i,r,a){var d=i.$on("$md-resize-enable",function(){d();var s=e.getComputedStyle(r[0]);i.$watch(a[t],function(e){if(!!e===n){o.nextTick(function(){i.$broadcast("$md-resize")});var t={cachedTransitionStyles:s};o.dom.animator.waitTransitionEnd(r,t).then(function(){i.$broadcast("$md-resize")})}})})}}}]}t.module("material.components.showHide",["material.core"]).directive("ngShow",n("ngShow",!0)).directive("ngHide",n("ngHide",!1))}(),function(){function e(e,o,i,r){function a(e,n){var r=function(){return!1},a=function(){return i.when(o.supplant(c,[n||""]))};return t.extend({isLockedOpen:r,isOpen:r,toggle:a,open:a,close:a,then:function(e){return s(n).then(e||t.noop)}},e)}function d(t,i){var a=e.get(t);return a||i?a:(r.error(o.supplant(c,[t||""])),n)}function s(t){return e.when(t)["catch"](r.error)}var c="SideNav '{0}' is not available! Did you use md-component-id='{0}'?",l={find:d,waitFor:s};return function(e,n){if(t.isUndefined(e))return l;var o=n===!0,i=l.find(e,o);return!i&&o?l.waitFor(e):!i&&t.isUndefined(n)?a(l,e):i}}function o(){return{restrict:"A",require:"^mdSidenav",link:function(e,t,n,o){}}}function i(e,o,i,r,a,d,s,c,l,m){function u(d,u,h,p){function f(e,t){d.isLockedOpen=e,e===t?u.toggleClass("_md-locked-open",!!e):a[e?"addClass":"removeClass"](u,"_md-locked-open"),M&&M.toggleClass("_md-locked-open",!!e)}function g(e){var t=o.findFocusTarget(u)||o.findFocusTarget(u,"[md-sidenav-focus]")||u,n=u.parent();n[e?"on":"off"]("keydown",$),M&&M[e?"on":"off"]("click",C);var i=b(n,e);return e&&(A=m[0].activeElement),v(e),T=l.all([e&&M?a.enter(M,n):M?a.leave(M):l.when(!0),a[e?"removeClass":"addClass"](u,"_md-closed")]).then(function(){d.isOpen&&t&&t.focus(),i&&i()})}function b(e,t){var n=u[0],o=e[0].scrollTop;if(t&&o){_={top:n.style.top,bottom:n.style.bottom,height:n.style.height};var i={top:o+"px",bottom:"initial",height:e[0].clientHeight+"px"};u.css(i),M.css(i)}return!t&&_?function(){n.style.top=_.top,n.style.bottom=_.bottom,n.style.height=_.height,M[0].style.top=null,M[0].style.bottom=null,M[0].style.height=null,_=null}:void 0}function v(e){var o=u.parent();e&&!y?(y=o.css("overflow"),o.css("overflow","hidden")):t.isDefined(y)&&(o.css("overflow",y),y=n)}function E(e){return d.isOpen==e?l.when(!0):l(function(t){d.isOpen=e,o.nextTick(function(){T.then(function(e){d.isOpen||(A&&A.focus(),A=null),t(e)})})})}function $(e){var t=e.keyCode===i.KEY_CODE.ESCAPE;return t?C(e):l.when(!0)}function C(e){return e.preventDefault(),p.close()}var y,M,_,A=null,T=l.when(!0),w=s(h.mdIsLockedOpen),k=function(){return w(d.$parent,{$media:function(t){return c.warn("$media is deprecated for is-locked-open. Use $mdMedia instead."),e(t)},$mdMedia:e})};t.isDefined(h.mdDisableBackdrop)||(M=o.createBackdrop(d,"_md-sidenav-backdrop md-opaque ng-enter")),u.addClass("_md"),r(u),M&&r.inherit(M,u),u.on("$destroy",function(){M&&M.remove(),p.destroy()}),d.$on("$destroy",function(){M&&M.remove()}),d.$watch(k,f),d.$watch("isOpen",g),p.$toggleOpen=E}return{restrict:"E",scope:{isOpen:"=?mdIsOpen"},controller:"$mdSidenavController",compile:function(e){return e.addClass("_md-closed"),e.attr("tabIndex","-1"),u}}}function r(e,t,n,o,i){var r=this;r.isOpen=function(){return!!e.isOpen},r.isLockedOpen=function(){return!!e.isLockedOpen},r.open=function(){return r.$toggleOpen(!0)},r.close=function(){return r.$toggleOpen(!1)},r.toggle=function(){return r.$toggleOpen(!e.isOpen)},r.$toggleOpen=function(t){return i.when(e.isOpen=t)},r.destroy=o.register(r,n.mdComponentId)}t.module("material.components.sidenav",["material.core","material.components.backdrop"]).factory("$mdSidenav",e).directive("mdSidenav",i).directive("mdSidenavFocus",o).controller("$mdSidenavController",r),e.$inject=["$mdComponentRegistry","$mdUtil","$q","$log"],i.$inject=["$mdMedia","$mdUtil","$mdConstant","$mdTheming","$animate","$compile","$parse","$log","$q","$document"],r.$inject=["$scope","$element","$attrs","$mdComponentRegistry","$q"]}(),function(){function e(){return{controller:function(){},compile:function(e){var o=e.find("md-slider");if(o){var i=o.attr("md-vertical");return i!==n&&e.attr("md-vertical",""),o.attr("flex")||o.attr("flex",""),function(e,n,o,i){function r(e){n.children().attr("disabled",e),n.find("input").attr("disabled",e)}n.addClass("_md");var a=t.noop;o.disabled?r(!0):o.ngDisabled&&(a=e.$watch(o.ngDisabled,function(e){r(e)})),e.$on("$destroy",function(){a()});var d;i.fitInputWidthToTextLength=function(e){var t=n[0].querySelector("md-input-container");if(t){var o=getComputedStyle(t),i=parseInt(o.minWidth),r=2*parseInt(o.padding);d=d||parseInt(o.maxWidth);var a=Math.max(d,i+r+i/2*e);t.style.maxWidth=a+"px"}}}}}}}function o(e,n,o,i,r,a,d,s,c,l){function m(e,n){var i=t.element(e[0].getElementsByClassName("_md-slider-wrapper")),r=n.tabindex||0;return i.attr("tabindex",r),(n.disabled||n.ngDisabled)&&i.attr("tabindex",-1),i.attr("role","slider"),o.expect(e,"aria-label"),u}function u(o,m,u,h){function p(){y(),x()}function f(e){se=parseFloat(e),m.attr("aria-valuemin",e),p()}function g(e){ce=parseFloat(e),m.attr("aria-valuemax",e),p()}function b(e){le=parseFloat(e)}function v(e){me=N(parseInt(e),0,6)}function E(){m.attr("aria-disabled",!!Y())}function $(){if(ie&&!Y()&&!t.isUndefined(le)){if(0>=le){var e="Slider step value must be greater than zero when in discrete mode";throw c.error(e),new Error(e)}var o=Math.floor((ce-se)/le);ue||(ue=t.element("<canvas>").css("position","absolute"),J.append(ue),he=ue[0].getContext("2d"));var i=M();!i||i.height||i.width||(y(),i=pe),ue[0].width=i.width,ue[0].height=i.height;for(var r,a=0;o>=a;a++){var d=n.getComputedStyle(J[0]);he.fillStyle=d.color||"black",r=Math.floor((oe?i.height:i.width)*(a/o)),he.fillRect(oe?0:r-1,oe?r-1:0,oe?i.width:2,oe?2:i.height)}}}function C(){if(ue&&he){var e=M();he.clearRect(0,0,e.width,e.height)}}function y(){pe=Q[0].getBoundingClientRect()}function M(){return te(),pe}function _(e){if(!Y()){var t;(oe?e.keyCode===r.KEY_CODE.DOWN_ARROW:e.keyCode===r.KEY_CODE.LEFT_ARROW)?t=-le:(oe?e.keyCode===r.KEY_CODE.UP_ARROW:e.keyCode===r.KEY_CODE.RIGHT_ARROW)&&(t=le),t=re?-t:t,t&&((e.metaKey||e.ctrlKey||e.altKey)&&(t*=4),e.preventDefault(),e.stopPropagation(),o.$evalAsync(function(){k(W.$viewValue+t)}))}}function A(){$(),o.mouseActive=!0,ee.removeClass("md-focused"),l(function(){o.mouseActive=!1},100)}function T(){o.mouseActive===!1&&ee.addClass("md-focused")}function w(){ee.removeClass("md-focused"),m.removeClass("_md-active"),C()}function k(e){W.$setViewValue(N(S(e)))}function x(){isNaN(W.$viewValue)&&(W.$viewValue=W.$modelValue),W.$viewValue=N(W.$viewValue);var e=q(W.$viewValue);o.modelValue=W.$viewValue,m.attr("aria-valuenow",W.$viewValue),D(e),G.text(W.$viewValue)}function N(e,n,o){return t.isNumber(e)?(n=t.isNumber(n)?n:se,o=t.isNumber(o)?o:ce,Math.max(n,Math.min(o,e))):void 0}function S(e){if(t.isNumber(e)){var n=Math.round((e-se)/le)*le+se;return n=Math.round(n*Math.pow(10,me))/Math.pow(10,me),V&&V.fitInputWidthToTextLength&&i.debounce(function(){V.fitInputWidthToTextLength(n.toString().length)},100)(),n}}function D(e){e=U(e);var t=100*e+"%",n=re?100*(1-e)+"%":t;X.css(oe?"bottom":"left",t),Z.css(oe?"height":"width",n),m.toggleClass(re?"_md-max":"_md-min",0===e),m.toggleClass(re?"_md-min":"_md-max",1===e)}function H(e){if(!Y()){m.addClass("_md-active"),m[0].focus(),y();var t=z(j(oe?e.pointer.y:e.pointer.x)),n=N(S(t));o.$apply(function(){k(n),D(q(n))})}}function I(e){if(!Y()){m.removeClass("_md-dragging");var t=z(j(oe?e.pointer.y:e.pointer.x)),n=N(S(t));o.$apply(function(){k(n),x()})}}function O(e){Y()||(fe=!0,e.stopPropagation(),m.addClass("_md-dragging"),P(e))}function R(e){fe&&(e.stopPropagation(),P(e))}function L(e){fe&&(e.stopPropagation(),fe=!1)}function P(e){ie?B(oe?e.pointer.y:e.pointer.x):F(oe?e.pointer.y:e.pointer.x)}function F(e){o.$evalAsync(function(){k(z(j(e)))})}function B(e){var t=z(j(e)),n=N(S(t));D(j(e)),G.text(n)}function U(e){return Math.max(0,Math.min(e||0,1))}function j(e){var t=oe?pe.top:pe.left,n=oe?pe.height:pe.width,o=(e-t)/n;return Math.max(0,Math.min(1,oe?1-o:o))}function z(e){var t=re?1-e:e;return se+t*(ce-se)}function q(e){var t=(e-se)/(ce-se);return re?1-t:t}a(m);var W=h[0]||{$setViewValue:function(e){this.$viewValue=e,this.$viewChangeListeners.forEach(function(e){e()})},$parsers:[],$formatters:[],$viewChangeListeners:[]},V=h[1],Y=(t.element(i.getClosest(m,"_md-slider-container",!0)),u.ngDisabled?t.bind(null,s(u.ngDisabled),o.$parent):function(){return m[0].hasAttribute("disabled"); +}),K=t.element(m[0].querySelector("._md-thumb")),G=t.element(m[0].querySelector("._md-thumb-text")),X=K.parent(),Q=t.element(m[0].querySelector("._md-track-container")),Z=t.element(m[0].querySelector("._md-track-fill")),J=t.element(m[0].querySelector("._md-track-ticks")),ee=t.element(m[0].getElementsByClassName("_md-slider-wrapper")),te=(t.element(m[0].getElementsByClassName("_md-slider-content")),i.throttle(y,5e3)),ne=3,oe=t.isDefined(u.mdVertical),ie=t.isDefined(u.mdDiscrete),re=t.isDefined(u.mdInvert);t.isDefined(u.min)?u.$observe("min",f):f(0),t.isDefined(u.max)?u.$observe("max",g):g(100),t.isDefined(u.step)?u.$observe("step",b):b(1),t.isDefined(u.round)?u.$observe("round",v):v(ne);var ae=t.noop;u.ngDisabled&&(ae=o.$parent.$watch(u.ngDisabled,E)),d.register(ee,"drag",{horizontal:!oe}),o.mouseActive=!1,ee.on("keydown",_).on("mousedown",A).on("focus",T).on("blur",w).on("$md.pressdown",H).on("$md.pressup",I).on("$md.dragstart",O).on("$md.drag",R).on("$md.dragend",L),setTimeout(p,0);var de=e.throttle(p);t.element(n).on("resize",de),o.$on("$destroy",function(){t.element(n).off("resize",de)}),W.$render=x,W.$viewChangeListeners.push(x),W.$formatters.push(N),W.$formatters.push(S);var se,ce,le,me,ue,he,pe={};y();var fe=!1}return{scope:{},require:["?ngModel","?^mdSliderContainer"],template:'<div class="_md-slider-wrapper"><div class="_md-slider-content"><div class="_md-track-container"><div class="_md-track"></div><div class="_md-track _md-track-fill"></div><div class="_md-track-ticks"></div></div><div class="_md-thumb-container"><div class="_md-thumb"></div><div class="_md-focus-thumb"></div><div class="_md-focus-ring"></div><div class="_md-sign"><span class="_md-thumb-text"></span></div><div class="_md-disabled-thumb"></div></div></div></div>',compile:m}}t.module("material.components.slider",["material.core"]).directive("mdSlider",o).directive("mdSliderContainer",e),o.$inject=["$$rAF","$window","$mdAria","$mdUtil","$mdConstant","$mdTheming","$mdGesture","$parse","$log","$timeout"]}(),function(){function e(e,o,i,r,a){function d(e){function t(e,t){t.addClass("_md-sticky-clone");var n={element:e,clone:t};return f.items.push(n),r.nextTick(function(){h.prepend(n.clone)}),p(),function(){f.items.forEach(function(t,n){t.element[0]===e[0]&&(f.items.splice(n,1),t.clone.remove())}),p()}}function a(){f.items.forEach(d),f.items=f.items.sort(function(e,t){return e.top<t.top?-1:1});for(var e,t=h.prop("scrollTop"),n=f.items.length-1;n>=0;n--)if(t>f.items[n].top){e=f.items[n];break}l(e)}function d(e){var t=e.element[0];for(e.top=0,e.left=0,e.right=0;t&&t!==h[0];)e.top+=t.offsetTop,e.left+=t.offsetLeft,t.offsetParent&&(e.right+=t.offsetParent.offsetWidth-t.offsetWidth-t.offsetLeft),t=t.offsetParent;e.height=e.element.prop("offsetHeight");var o=r.floatingScrollbars()?"0":n;r.bidi(e.clone,"margin-left",e.left,o),r.bidi(e.clone,"margin-right",o,e.right)}function s(){var e=h.prop("scrollTop"),t=e>(s.prevScrollTop||0);if(s.prevScrollTop=e,0===e)return void l(null);if(t){if(f.next&&f.next.top<=e)return void l(f.next);if(f.current&&f.next&&f.next.top-e<=f.next.height)return void u(f.current,e+(f.next.top-f.next.height-e))}if(!t){if(f.current&&f.prev&&e<f.current.top)return void l(f.prev);if(f.next&&f.current&&e>=f.next.top-f.current.height)return void u(f.current,e+(f.next.top-e-f.current.height))}f.current&&u(f.current,e)}function l(e){if(f.current!==e){f.current&&(u(f.current,null),m(f.current,null)),e&&m(e,"active"),f.current=e;var t=f.items.indexOf(e);f.next=f.items[t+1],f.prev=f.items[t-1],m(f.next,"next"),m(f.prev,"prev")}}function m(e,t){e&&e.state!==t&&(e.state&&(e.clone.attr("sticky-prev-state",e.state),e.element.attr("sticky-prev-state",e.state)),e.clone.attr("sticky-state",t),e.element.attr("sticky-state",t),e.state=t)}function u(e,t){e&&(null===t||t===n?e.translateY&&(e.translateY=null,e.clone.css(o.CSS.TRANSFORM,"")):(e.translateY=t,r.bidi(e.clone,o.CSS.TRANSFORM,"translate3d("+e.left+"px,"+t+"px,0)","translateY("+t+"px)")))}var h=e.$element,p=i.throttle(a);c(h),h.on("$scrollstart",p),h.on("$scroll",s);var f;return f={prev:null,current:null,next:null,items:[],add:t,refreshElements:a}}function s(n){var o,i=t.element("<div>");e[0].body.appendChild(i[0]);for(var r=["sticky","-webkit-sticky"],a=0;a<r.length;++a)if(i.css({position:r[a],top:0,"z-index":2}),i.css("position")==r[a]){o=r[a];break}return i.remove(),o}function c(e){function t(){+r.now()-o>a?(n=!1,e.triggerHandler("$scrollend")):(e.triggerHandler("$scroll"),i.throttle(t))}var n,o,a=200;e.on("scroll touchmove",function(){n||(n=!0,i.throttle(t),e.triggerHandler("$scrollstart")),e.triggerHandler("$scroll"),o=+r.now()})}var l=s();return function(e,t,n){var o=t.controller("mdContent");if(o)if(l)t.css({position:l,top:0,"z-index":2});else{var i=o.$element.data("$$sticky");i||(i=d(o),o.$element.data("$$sticky",i));var r=n||a(t.clone())(e),s=i.add(t,r);e.$on("$destroy",s)}}}t.module("material.components.sticky",["material.core","material.components.content"]).factory("$mdSticky",e),e.$inject=["$document","$mdConstant","$$rAF","$mdUtil","$compile"]}(),function(){function e(e,n,o,i){return{restrict:"E",replace:!0,transclude:!0,template:'<div class="md-subheader _md"> <div class="_md-subheader-inner"> <div class="_md-subheader-content"></div> </div></div>',link:function(n,i,r,a,d){function s(e){return t.element(e[0].querySelector("._md-subheader-content"))}o(i),i.addClass("_md");var c=i[0].outerHTML;d(n,function(e){s(i).append(e)}),i.hasClass("md-no-sticky")||d(n,function(o){var r=t.element('<div class="_md-subheader-wrapper">'+c+"</div>");s(r).append(o),e(n,i,r)})}}}t.module("material.components.subheader",["material.core","material.components.sticky"]).directive("mdSubheader",e),e.$inject=["$mdSticky","$compile","$mdTheming","$mdUtil"]}(),function(){function e(e){function t(e){function t(t,i,r){var a=e(r[n]);i.on(o,function(e){t.$applyAsync(function(){a(t,{$event:e})})})}return{restrict:"A",link:t}}var n="md"+e,o="$md."+e.toLowerCase();return t.$inject=["$parse"],t}t.module("material.components.swipe",["material.core"]).directive("mdSwipeLeft",e("SwipeLeft")).directive("mdSwipeRight",e("SwipeRight")).directive("mdSwipeUp",e("SwipeUp")).directive("mdSwipeDown",e("SwipeDown"))}(),function(){function e(e,n,o,i,r,a){function d(e,d){var c=s.compile(e,d);return e.addClass("_md-dragging"),function(e,d,s,l){function m(t){f&&f(e)||(t.stopPropagation(),d.addClass("_md-dragging"),v={width:g.prop("offsetWidth")})}function u(e){if(v){e.stopPropagation(),e.srcEvent&&e.srcEvent.preventDefault();var t=e.pointer.distanceX/v.width,n=l.$viewValue?1+t:t;n=Math.max(0,Math.min(1,n)),g.css(o.CSS.TRANSFORM,"translate3d("+100*n+"%,0,0)"),v.translate=n}}function h(e){if(v){e.stopPropagation(),d.removeClass("_md-dragging"),g.css(o.CSS.TRANSFORM,"");var t=l.$viewValue?v.translate>.5:v.translate<.5;t&&p(!l.$viewValue),v=null}}function p(t){e.$apply(function(){l.$setViewValue(t),l.$render()})}l=l||n.fakeNgModel();var f=null;null!=s.disabled?f=function(){return!0}:s.ngDisabled&&(f=i(s.ngDisabled));var g=t.element(d[0].querySelector("._md-thumb-container")),b=t.element(d[0].querySelector("._md-container"));r(function(){d.removeClass("_md-dragging")}),c(e,d,s,l),f&&e.$watch(f,function(e){d.attr("tabindex",e?-1:0)}),a.register(b,"drag"),b.on("$md.dragstart",m).on("$md.drag",u).on("$md.dragend",h);var v}}var s=e[0];return{restrict:"E",priority:210,transclude:!0,template:'<div class="_md-container"><div class="_md-bar"></div><div class="_md-thumb-container"><div class="_md-thumb" md-ink-ripple md-ink-ripple-checkbox></div></div></div><div ng-transclude class="_md-label"></div>',require:"?ngModel",compile:d}}t.module("material.components.switch",["material.core","material.components.checkbox"]).directive("mdSwitch",e),e.$inject=["mdCheckboxDirective","$mdUtil","$mdConstant","$parse","$$rAF","$mdGesture"]}(),function(){t.module("material.components.tabs",["material.core","material.components.icon"])}(),function(){function e(e){return{restrict:"E",link:function(t,n){n.addClass("_md"),t.$on("$destroy",function(){e.destroy()})}}}function n(e){function n(e){i=e}function o(e,n,o,r){function a(t,a,d){i=d.textContent||d.content;var l=!r("gt-sm");return a=o.extractElementByName(a,"md-toast",!0),d.element=a,d.onSwipe=function(e,t){var i=e.type.replace("$md.",""),r=i.replace("swipe","");"down"===r&&-1!=d.position.indexOf("top")&&!l||"up"===r&&(-1!=d.position.indexOf("bottom")||l)||("left"!==r&&"right"!==r||!l)&&(a.addClass("_md-"+i),o.nextTick(n.cancel))},d.openClass=s(d.position),d.parent.addClass(d.openClass),o.hasComputedStyle(d.parent,"position","static")&&d.parent.css("position","relative"),a.on(c,d.onSwipe),a.addClass(l?"_md-bottom":d.position.split(" ").map(function(e){return"_md-"+e}).join(" ")),d.parent&&d.parent.addClass("_md-toast-animating"),e.enter(a,d.parent).then(function(){d.parent&&d.parent.removeClass("_md-toast-animating")})}function d(t,n,i){return n.off(c,i.onSwipe),i.parent&&i.parent.addClass("_md-toast-animating"),i.openClass&&i.parent.removeClass(i.openClass),(1==i.$destroy?n.remove():e.leave(n)).then(function(){i.parent&&i.parent.removeClass("_md-toast-animating"),o.hasComputedStyle(i.parent,"position","static")&&i.parent.css("position","")})}function s(e){return r("gt-xs")?"_md-toast-open-"+(e.indexOf("top")>-1?"top":"bottom"):"_md-toast-open-bottom"}var c="$md.swipeleft $md.swiperight $md.swipeup $md.swipedown";return{onShow:a,onRemove:d,position:"bottom left",themable:!0,hideDelay:3e3,autoWrap:!0,transformTemplate:function(e,n){var o=n.autoWrap&&e&&!/md-toast-content/g.test(e);if(o){var i=document.createElement("md-template");i.innerHTML=e;for(var r=0;r<i.children.length;r++)if("MD-TOAST"===i.children[r].nodeName){var a=t.element('<div class="md-toast-content">');a.append(t.element(i.children[r].childNodes)),i.children[r].appendChild(a[0])}return i.outerHTML}return e||""}}}var i,r="ok",a=e("$mdToast").setDefaults({methods:["position","hideDelay","capsule","parent","position"],options:o}).addPreset("simple",{argOption:"textContent",methods:["textContent","content","action","highlightAction","highlightClass","theme","parent"],options:["$mdToast","$mdTheming",function(e,t){return{template:'<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}"> <div class="md-toast-content"> <span flex class="md-toast-text" role="alert" aria-relevant="all" aria-atomic="true"> {{ toast.content }} </span> <md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ng-class="highlightClasses"> {{ toast.action }} </md-button> </div></md-toast>',controller:["$scope",function(t){var n=this;n.highlightAction&&(t.highlightClasses=["md-highlight",n.highlightClass]),t.$watch(function(){return i},function(){n.content=i}),this.resolve=function(){e.hide(r)}}],theme:t.defaultTheme(),controllerAs:"toast",bindToController:!0}}]}).addMethod("updateTextContent",n).addMethod("updateContent",n);return o.$inject=["$animate","$mdToast","$mdUtil","$mdMedia"],a}t.module("material.components.toast",["material.core","material.components.button"]).directive("mdToast",e).provider("$mdToast",n),e.$inject=["$mdToast"],n.$inject=["$$interimElementProvider"]}(),function(){function e(e,n,o,i,r,a,d,s,c,l){function m(d,m,p){function f(){d.delay=d.delay||u}function g(){var e="center top";switch(d.direction){case"left":e="right center";break;case"right":e="left center";break;case"top":e="center bottom";break;case"bottom":e="center top"}k.css("transform-origin",e)}function b(e){e?M():_()}function v(){if(m[0]&&"MutationObserver"in n){var e=new MutationObserver(function(e){e.forEach(function(e){"md-visible"===e.attributeName&&(d.visibleWatcher||(d.visibleWatcher=d.$watch("visible",b))),"md-direction"===e.attributeName&&A(d.direction)})});e.observe(m[0],{attributes:!0}),p.hasOwnProperty("mdVisible")&&(d.visibleWatcher=d.$watch("visible",b))}else d.visibleWatcher=d.$watch("visible",b),d.$watch("direction",A);var t=function(){d.$destroy()};m.one("$destroy",t),w.one("$destroy",t),d.$on("$destroy",function(){y(!1),m.remove(),e&&e.disconnect()}),m.text().indexOf(l.startSymbol())>-1&&d.$watch(function(){return m.text().trim()},E)}function E(e){if((e||!w.attr("aria-label"))&&!w.text().trim()){var t=e||m.text().trim(),n=l(t)(w.scope());w.attr("aria-label",n)}}function $(){m.detach(),m.attr("role","tooltip")}function C(){function o(){y(!1)}var a=!1;if(w[0]&&"MutationObserver"in n){var s=new MutationObserver(function(e){e.some(function(e){return"disabled"===e.attributeName&&w[0].disabled})&&r.nextTick(function(){y(!1)})});s.observe(w[0],{attributes:!0})}var c=function(){l=document.activeElement===w[0]},l=!1;t.element(n).on("blur",c).on("resize",S),document.addEventListener("scroll",o,!0),d.$on("$destroy",function(){t.element(n).off("blur",c).off("resize",S),w.off("focus mouseenter touchstart",m).off("blur mouseleave touchend touchcancel",u).off("mousedown",h),u(),document.removeEventListener("scroll",o,!0),s&&s.disconnect()});var m=function(e){return"focus"===e.type&&l?void(l=!1):(w.on("blur mouseleave touchend touchcancel",u),void y(!0))},u=function(){var t=d.hasOwnProperty("autohide")?d.autohide:p.hasOwnProperty("mdAutohide");(t||a||i[0].activeElement!==w[0])&&(N&&(e.cancel(N),y.queued=!1,N=null),w.off("blur mouseleave touchend touchcancel",u),w.triggerHandler("blur"),y(!1)),a=!1},h=function(){a=!0};w.on("mousedown",h),w.on("focus mouseenter touchstart",m)}function y(t){y.queued&&y.value===!!t||!y.queued&&d.visible===!!t||(y.value=!!t,y.queued||(t?(y.queued=!0,N=e(function(){d.visible=y.value,y.queued=!1,N=null,d.visibleWatcher||b(d.visible)},d.delay)):r.nextTick(function(){d.visible=!1,d.visibleWatcher||b(!1)})))}function M(){if(m[0].textContent.trim()){if(m.css({top:0,left:0}),x.append(m),r.hasComputedStyle(m,"display","none"))return d.visible=!1,void m.detach();A(),t.forEach([m,k],function(e){s.addClass(e,"_md-show")})}}function _(){var e=[];t.forEach([m,k],function(t){t.parent()&&t.hasClass("_md-show")&&e.push(s.removeClass(t,"_md-show"))}),c.all(e).then(function(){d.visible||m.detach()})}function A(){d.visible&&(g(),T())}function T(){function e(e){var t={left:e.left,top:e.top};return t.left=Math.min(t.left,x.prop("scrollWidth")-n.width-h),t.left=Math.max(t.left,h),t.top=Math.min(t.top,x.prop("scrollHeight")-n.height-h),t.top=Math.max(t.top,h),t}function t(e){return"left"===e?{left:o.left-n.width-h,top:o.top+o.height/2-n.height/2}:"right"===e?{left:o.left+o.width+h,top:o.top+o.height/2-n.height/2}:"top"===e?{left:o.left+o.width/2-n.width/2,top:o.top-n.height-h}:{left:o.left+o.width/2-n.width/2,top:o.top+o.height+h}}var n=r.offsetRect(m,x),o=r.offsetRect(w,x),i=t(d.direction),a=m.prop("offsetParent");d.direction?i=e(i):a&&i.top>a.scrollHeight-n.height-h&&(i=e(t("top"))),m.css({left:i.left+"px",top:i.top+"px"})}a(m);var w=r.getParentWithPointerEvents(m),k=t.element(m[0].getElementsByClassName("_md-content")[0]),x=t.element(document.body),N=null,S=o.throttle(function(){A()});s.pin&&s.pin(m,w),f(),$(),C(),g(),v(),E()}var u=0,h=8;return{restrict:"E",transclude:!0,priority:210,template:'<div class="_md-content _md" ng-transclude></div>',scope:{delay:"=?mdDelay",visible:"=?mdVisible",autohide:"=?mdAutohide",direction:"@?mdDirection"},compile:function(e,t){return t.mdDirection||t.$set("mdDirection","bottom"),m}}}t.module("material.components.tooltip",["material.core"]).directive("mdTooltip",e),e.$inject=["$timeout","$window","$$rAF","$document","$mdUtil","$mdTheming","$rootElement","$animate","$q","$interpolate"]}(),function(){function e(){return{controller:o,template:n,compile:function(e,t){e.addClass("md-virtual-repeat-container").addClass(t.hasOwnProperty("mdOrientHorizontal")?"md-orient-horizontal":"md-orient-vertical")}}}function n(e){return'<div class="md-virtual-repeat-scroller"><div class="md-virtual-repeat-sizer"></div><div class="md-virtual-repeat-offsetter">'+e[0].innerHTML+"</div></div>"}function o(e,n,o,i,r,a,d,s){this.$rootScope=i,this.$scope=a,this.$element=d,this.$attrs=s,this.size=0,this.scrollSize=0,this.scrollOffset=0,this.horizontal=this.$attrs.hasOwnProperty("mdOrientHorizontal"),this.repeater=null,this.autoShrink=this.$attrs.hasOwnProperty("mdAutoShrink"),this.autoShrinkMin=parseInt(this.$attrs.mdAutoShrinkMin,10)||0,this.originalSize=null,this.offsetSize=parseInt(this.$attrs.mdOffsetSize,10)||0,this.oldElementSize=null,this.$attrs.mdTopIndex?(this.bindTopIndex=o(this.$attrs.mdTopIndex),this.topIndex=this.bindTopIndex(this.$scope),t.isDefined(this.topIndex)||(this.topIndex=0,this.bindTopIndex.assign(this.$scope,0)),this.$scope.$watch(this.bindTopIndex,t.bind(this,function(e){e!==this.topIndex&&this.scrollToIndex(e)}))):this.topIndex=0,this.scroller=d[0].getElementsByClassName("md-virtual-repeat-scroller")[0],this.sizer=this.scroller.getElementsByClassName("md-virtual-repeat-sizer")[0],this.offsetter=this.scroller.getElementsByClassName("md-virtual-repeat-offsetter")[0];var c=t.bind(this,this.updateSize);e(t.bind(this,function(){c();var e=n.debounce(c,10,null,!1),o=t.element(r);this.size||e(),o.on("resize",e),a.$on("$destroy",function(){o.off("resize",e)}),a.$emit("$md-resize-enable"),a.$on("$md-resize",c)}))}function i(e){return{controller:r,priority:1e3,require:["mdVirtualRepeat","^^mdVirtualRepeatContainer"],restrict:"A",terminal:!0,transclude:"element",compile:function(t,n){var o=n.mdVirtualRepeat,i=o.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)\s*$/),r=i[1],a=e(i[2]),d=n.mdExtraName&&e(n.mdExtraName);return function(e,t,n,o,i){o[0].link_(o[1],i,r,a,d)}}}}function r(e,n,o,i,r,a,d,s){this.$scope=e,this.$element=n,this.$attrs=o,this.$browser=i,this.$document=r,this.$rootScope=a,this.$$rAF=d,this.onDemand=s.parseAttributeBoolean(o.mdOnDemand),this.browserCheckUrlChange=i.$$checkUrlChange,this.newStartIndex=0,this.newEndIndex=0,this.newVisibleEnd=0,this.startIndex=0,this.endIndex=0,this.itemSize=e.$eval(o.mdItemSize)||null,this.isFirstRender=!0,this.isVirtualRepeatUpdating_=!1,this.itemsLength=0,this.unwatchItemSize_=t.noop,this.blocks={},this.pooledBlocks=[],e.$on("$destroy",t.bind(this,this.cleanupBlocks_))}function a(e){if(!t.isFunction(e.getItemAtIndex)||!t.isFunction(e.getLength))throw Error("When md-on-demand is enabled, the Object passed to md-virtual-repeat must implement functions getItemAtIndex() and getLength() ");this.model=e}t.module("material.components.virtualRepeat",["material.core","material.components.showHide"]).directive("mdVirtualRepeatContainer",e).directive("mdVirtualRepeat",i);var d=1533917,s=3;o.$inject=["$$rAF","$mdUtil","$parse","$rootScope","$window","$scope","$element","$attrs"],o.prototype.register=function(e){this.repeater=e,t.element(this.scroller).on("scroll wheel touchmove touchend",t.bind(this,this.handleScroll_))},o.prototype.isHorizontal=function(){return this.horizontal},o.prototype.getSize=function(){return this.size},o.prototype.setSize_=function(e){var t=this.getDimensionName_();this.size=e,this.$element[0].style[t]=e+"px"},o.prototype.unsetSize_=function(){this.$element[0].style[this.getDimensionName_()]=this.oldElementSize,this.oldElementSize=null},o.prototype.updateSize=function(){this.originalSize||(this.size=this.isHorizontal()?this.$element[0].clientWidth:this.$element[0].clientHeight,this.handleScroll_(),this.repeater&&this.repeater.containerUpdated())},o.prototype.getScrollSize=function(){return this.scrollSize},o.prototype.getDimensionName_=function(){return this.isHorizontal()?"width":"height"},o.prototype.sizeScroller_=function(e){var t=this.getDimensionName_(),n=this.isHorizontal()?"height":"width";if(this.sizer.innerHTML="",d>e)this.sizer.style[t]=e+"px";else{this.sizer.style[t]="auto",this.sizer.style[n]="auto";var o=Math.floor(e/d),i=document.createElement("div");i.style[t]="1533917px",i.style[n]="1px";for(var r=0;o>r;r++)this.sizer.appendChild(i.cloneNode(!1));i.style[t]=e-o*d+"px",this.sizer.appendChild(i)}},o.prototype.autoShrink_=function(e){var t=Math.max(e,this.autoShrinkMin*this.repeater.getItemSize());if(this.autoShrink&&t!==this.size){null===this.oldElementSize&&(this.oldElementSize=this.$element[0].style[this.getDimensionName_()]);var n=this.originalSize||this.size;if(!n||n>t)this.originalSize||(this.originalSize=this.size),this.setSize_(t);else if(null!==this.originalSize){this.unsetSize_();var o=this.originalSize;this.originalSize=null,o||this.updateSize(),this.setSize_(o||this.size)}this.repeater.containerUpdated()}},o.prototype.setScrollSize=function(e){var t=e+this.offsetSize;this.scrollSize!==t&&(this.sizeScroller_(t),this.autoShrink_(t),this.scrollSize=t)},o.prototype.getScrollOffset=function(){return this.scrollOffset},o.prototype.scrollTo=function(e){this.scroller[this.isHorizontal()?"scrollLeft":"scrollTop"]=e,this.handleScroll_()},o.prototype.scrollToIndex=function(e){var t=this.repeater.getItemSize(),n=this.repeater.itemsLength;e>n&&(e=n-1),this.scrollTo(t*e)},o.prototype.resetScroll=function(){this.scrollTo(0)},o.prototype.handleScroll_=function(){var e=t.element(document)[0],n="rtl"!=e.dir&&"rtl"!=e.body.dir;n||this.maxSize||(this.scroller.scrollLeft=this.scrollSize,this.maxSize=this.scroller.scrollLeft);var o=this.isHorizontal()?n?this.scroller.scrollLeft:this.maxSize-this.scroller.scrollLeft:this.scroller.scrollTop;if(!(o===this.scrollOffset||o>this.scrollSize-this.size)){var i=this.repeater.getItemSize();if(i){var r=Math.max(0,Math.floor(o/i)-s),a=(this.isHorizontal()?"translateX(":"translateY(")+(!this.isHorizontal()||n?r*i:-(r*i))+"px)";if(this.scrollOffset=o,this.offsetter.style.webkitTransform=a,this.offsetter.style.transform=a,this.bindTopIndex){var d=Math.floor(o/i);d!==this.topIndex&&d<this.repeater.getItemCount()&&(this.topIndex=d,this.bindTopIndex.assign(this.$scope,d),this.$rootScope.$$phase||this.$scope.$digest())}this.repeater.containerUpdated()}}},i.$inject=["$parse"],r.$inject=["$scope","$element","$attrs","$browser","$document","$rootScope","$$rAF","$mdUtil"],r.Block,r.prototype.link_=function(e,n,o,i,r){this.container=e,this.transclude=n,this.repeatName=o,this.rawRepeatListExpression=i,this.extraName=r,this.sized=!1,this.repeatListExpression=t.bind(this,this.repeatListExpression_),this.container.register(this)},r.prototype.cleanupBlocks_=function(){t.forEach(this.pooledBlocks,function(e){e.element.remove()})},r.prototype.readItemSize_=function(){if(!this.itemSize){this.items=this.repeatListExpression(this.$scope),this.parentNode=this.$element[0].parentNode;var e=this.getBlock_(0);e.element[0].parentNode||this.parentNode.appendChild(e.element[0]),this.itemSize=e.element[0][this.container.isHorizontal()?"offsetWidth":"offsetHeight"]||null,this.blocks[0]=e,this.poolBlock_(0),this.itemSize&&this.containerUpdated()}},r.prototype.repeatListExpression_=function(e){var t=this.rawRepeatListExpression(e);if(this.onDemand&&t){var n=new a(t);return n.$$includeIndexes(this.newStartIndex,this.newVisibleEnd),n}return t},r.prototype.containerUpdated=function(){return this.itemSize?(this.sized||(this.items=this.repeatListExpression(this.$scope)),this.sized||(this.unwatchItemSize_(),this.sized=!0,this.$scope.$watchCollection(this.repeatListExpression,t.bind(this,function(e,t){this.isVirtualRepeatUpdating_||this.virtualRepeatUpdate_(e,t)}))),this.updateIndexes_(),void((this.newStartIndex!==this.startIndex||this.newEndIndex!==this.endIndex||this.container.getScrollOffset()>this.container.getScrollSize())&&(this.items instanceof a&&this.items.$$includeIndexes(this.newStartIndex,this.newEndIndex),this.virtualRepeatUpdate_(this.items,this.items)))):(this.unwatchItemSize_&&this.unwatchItemSize_!==t.noop&&this.unwatchItemSize_(),this.unwatchItemSize_=this.$scope.$watchCollection(this.repeatListExpression,t.bind(this,function(e){e&&e.length&&this.$$rAF(t.bind(this,this.readItemSize_))})),void(this.$rootScope.$$phase||this.$scope.$digest()))},r.prototype.getItemSize=function(){return this.itemSize},r.prototype.getItemCount=function(){return this.itemsLength},r.prototype.virtualRepeatUpdate_=function(e,n){this.isVirtualRepeatUpdating_=!0;var o=e&&e.length||0,i=!1;if(this.items&&o<this.items.length&&0!==this.container.getScrollOffset())return this.items=e,void this.container.resetScroll();if(o!==this.itemsLength&&(i=!0,this.itemsLength=o),this.items=e,(e!==n||i)&&this.updateIndexes_(),this.parentNode=this.$element[0].parentNode,i&&this.container.setScrollSize(o*this.itemSize),this.isFirstRender){this.isFirstRender=!1;var r=this.$attrs.mdStartIndex?this.$scope.$eval(this.$attrs.mdStartIndex):this.container.topIndex;this.container.scrollToIndex(r)}Object.keys(this.blocks).forEach(function(e){var t=parseInt(e,10);(t<this.newStartIndex||t>=this.newEndIndex)&&this.poolBlock_(t)},this),this.$browser.$$checkUrlChange=t.noop;var a,d,s=[],c=[];for(a=this.newStartIndex;a<this.newEndIndex&&null==this.blocks[a];a++)d=this.getBlock_(a),this.updateBlock_(d,a),s.push(d);for(;null!=this.blocks[a];a++)this.updateBlock_(this.blocks[a],a);for(var l=a-1;a<this.newEndIndex;a++)d=this.getBlock_(a),this.updateBlock_(d,a),c.push(d);s.length&&this.parentNode.insertBefore(this.domFragmentFromBlocks_(s),this.$element[0].nextSibling),c.length&&this.parentNode.insertBefore(this.domFragmentFromBlocks_(c),this.blocks[l]&&this.blocks[l].element[0].nextSibling),this.$browser.$$checkUrlChange=this.browserCheckUrlChange,this.startIndex=this.newStartIndex,this.endIndex=this.newEndIndex,this.isVirtualRepeatUpdating_=!1},r.prototype.getBlock_=function(e){if(this.pooledBlocks.length)return this.pooledBlocks.pop();var n;return this.transclude(t.bind(this,function(t,o){n={element:t,"new":!0,scope:o},this.updateScope_(o,e),this.parentNode.appendChild(t[0])})),n},r.prototype.updateBlock_=function(e,t){this.blocks[t]=e,(e["new"]||e.scope.$index!==t||e.scope[this.repeatName]!==this.items[t])&&(e["new"]=!1,this.updateScope_(e.scope,t),this.$rootScope.$$phase||e.scope.$digest())},r.prototype.updateScope_=function(e,t){e.$index=t,e[this.repeatName]=this.items&&this.items[t],this.extraName&&(e[this.extraName(this.$scope)]=this.items[t])},r.prototype.poolBlock_=function(e){this.pooledBlocks.push(this.blocks[e]),this.parentNode.removeChild(this.blocks[e].element[0]),delete this.blocks[e]},r.prototype.domFragmentFromBlocks_=function(e){var t=this.$document[0].createDocumentFragment();return e.forEach(function(e){t.appendChild(e.element[0])}),t},r.prototype.updateIndexes_=function(){var e=this.items?this.items.length:0,t=Math.ceil(this.container.getSize()/this.itemSize);this.newStartIndex=Math.max(0,Math.min(e-t,Math.floor(this.container.getScrollOffset()/this.itemSize))),this.newVisibleEnd=this.newStartIndex+t+s,this.newEndIndex=Math.min(e,this.newVisibleEnd),this.newStartIndex=Math.max(0,this.newStartIndex-s)},a.prototype.$$includeIndexes=function(e,t){for(var n=e;t>n;n++)this.hasOwnProperty(n)||(this[n]=this.model.getItemAtIndex(n));this.length=this.model.getLength()}}(),function(){function e(e,n,o,i,r){var a=t.bind(null,o.supplant,"translate3d(0,{0}px,0)");return{template:"",restrict:"E",link:function(d,s,c){function l(){function i(e){var t=s.parent().find("md-content");!f&&t.length&&l(null,t),e=d.$eval(e),e===!1?g():g=u()}function l(e,t){t&&s.parent()[0]===t.parent()[0]&&(f&&f.off("scroll",$),f=t,g=u())}function m(e){var t=e?e.target.scrollTop:v;C(),b=Math.min(p/E,Math.max(0,b+t-v)),s.css(n.CSS.TRANSFORM,a([-b*E])),f.css(n.CSS.TRANSFORM,a([(p-b)*E])),v=t,o.nextTick(function(){var e=s.hasClass("md-whiteframe-z1");e&&!b?r.removeClass(s,"md-whiteframe-z1"):!e&&b&&r.addClass(s,"md-whiteframe-z1")})}function u(){return f?(f.on("scroll",$),f.attr("scroll-shrink","true"),o.nextTick(h,!1),function(){f.off("scroll",$),f.attr("scroll-shrink","false"),h()}):t.noop}function h(){p=s.prop("offsetHeight");var e=-p*E+"px";f.css({"margin-top":e,"margin-bottom":e}),m()}var p,f,g=t.noop,b=0,v=0,E=c.mdShrinkSpeedFactor||.5,$=e.throttle(m),C=o.debounce(h,5e3);d.$on("$mdContentLoaded",l),c.$observe("mdScrollShrink",i),c.ngShow&&d.$watch(c.ngShow,h),c.ngHide&&d.$watch(c.ngHide,h),d.$on("$destroy",g)}s.addClass("_md"),i(s),t.isDefined(c.mdScrollShrink)&&l()}}}t.module("material.components.toolbar",["material.core","material.components.content"]).directive("mdToolbar",e),e.$inject=["$$rAF","$mdConstant","$mdUtil","$mdTheming","$animate"]}(),function(){function e(e){function t(t,a,d){var s="";d.$observe("mdWhiteframe",function(t){t=parseInt(t,10)||r,t!=n&&(t>i||o>t)&&(e.warn("md-whiteframe attribute value is invalid. It should be a number between "+o+" and "+i,a[0]),t=r);var c=t==n?"":"md-whiteframe-"+t+"dp";d.$updateClass(c,s),s=c})}var n=-1,o=1,i=24,r=4;return{link:t}}t.module("material.components.whiteframe",["material.core"]).directive("mdWhiteframe",e),e.$inject=["$log"]}(),function(){function e(e,o,d,s,c,l,m,u,h,p){function f(){d.initOptionalProperties(e,h,{searchText:null,selectedItem:null}),c(o),E(),d.nextTick(function(){C(),b(),v(),o.on("focus",v)})}function g(){function t(){var e=0,t=o.find("md-input-container");if(t.length){var n=t.find("input");e=t.prop("offsetHeight"),e-=n.prop("offsetTop"),e-=n.prop("offsetHeight"),e+=t.prop("offsetTop")}return e}function n(){var e=he.scrollContainer.getBoundingClientRect(),t={};e.right>m.right-r&&(t.left=c.right-e.width+"px"),he.$.scrollContainer.css(t)}if(!he)return d.nextTick(g,!1,e);var s,c=he.wrap.getBoundingClientRect(),l=he.snap.getBoundingClientRect(),m=he.root.getBoundingClientRect(),u=l.bottom-m.top,p=m.bottom-l.top,f=c.left-m.left,b=c.width,v=t();h.mdFloatingLabel&&(f+=a,b-=2*a),s={left:f+"px",minWidth:b+"px",maxWidth:Math.max(c.right-m.left,m.right-c.left)-r+"px"},u>p&&m.height-c.bottom-r<i?(s.top="auto",s.bottom=p+"px",s.maxHeight=Math.min(i,c.top-m.top-r)+"px"):(s.top=u-v+"px",s.bottom="auto",s.maxHeight=Math.min(i,m.bottom+d.scrollTop()-c.bottom-r)+"px"),he.$.scrollContainer.css(s),d.nextTick(n,!1)}function b(){he.$.root.length&&(c(he.$.scrollContainer),he.$.scrollContainer.detach(),he.$.root.append(he.$.scrollContainer),m.pin&&m.pin(he.$.scrollContainer,u))}function v(){e.autofocus&&he.input.focus()}function E(){var n=parseInt(e.delay,10)||0;h.$observe("disabled",function(e){le.isDisabled=d.parseAttributeBoolean(e,!1)}),h.$observe("required",function(e){le.isRequired=d.parseAttributeBoolean(e,!1)}),h.$observe("readonly",function(e){le.isReadonly=d.parseAttributeBoolean(e,!1)}),e.$watch("searchText",n?d.debounce(O,n):O),e.$watch("selectedItem",x),t.element(l).on("resize",g),e.$on("$destroy",$)}function $(){if(le.hidden||d.enableScrolling(),t.element(l).off("resize",g),he){var e="ul scroller scrollContainer input".split(" ");t.forEach(e,function(e){he.$[e].remove()})}}function C(){he={main:o[0],scrollContainer:o[0].getElementsByClassName("md-virtual-repeat-container")[0],scroller:o[0].getElementsByClassName("md-virtual-repeat-scroller")[0],ul:o.find("ul")[0],input:o.find("input")[0],wrap:o.find("md-autocomplete-wrap")[0],root:document.body},he.li=he.ul.getElementsByTagName("li"),he.snap=y(),he.$=M(he)}function y(){for(var e=o;e.length;e=e.parent())if(t.isDefined(e.attr("md-autocomplete-snap")))return e[0];return he.wrap}function M(e){var n={};for(var o in e)e.hasOwnProperty(o)&&(n[o]=t.element(e[o]));return n}function _(n,o){!n&&o?(g(),he&&d.nextTick(function(){d.disableScrollAround(he.ul),$e=A(t.element(he.wrap))},!1,e)):n&&!o&&d.nextTick(function(){d.enableScrolling(),$e&&($e(),$e=null)},!1,e)}function A(e){function t(e){e.preventDefault()}return e.on("wheel",t),e.on("touchmove",t),function(){e.off("wheel",t),e.off("touchmove",t)}}function T(){fe=!0}function w(){be||he.input.focus(),fe=!1,le.hidden=W()}function k(){he.input.focus()}function x(t,n){t&&U(t).then(function(o){e.searchText=o,D(t,n)}),t!==n&&N()}function N(){t.isFunction(e.itemChange)&&e.itemChange(j(e.selectedItem))}function S(){t.isFunction(e.textChange)&&e.textChange()}function D(e,t){ge.forEach(function(n){n(e,t)})}function H(e){-1==ge.indexOf(e)&&ge.push(e)}function I(e){var t=ge.indexOf(e);-1!=t&&ge.splice(t,1)}function O(t,n){le.index=z(),t!==n&&U(e.selectedItem).then(function(o){t!==o&&(e.selectedItem=null,t!==n&&S(), +Q()?se():(le.matches=[],q(!1),ne()))})}function R(){be=!1,fe||(le.hidden=W())}function L(e){e&&(fe=!1,be=!1),he.input.blur()}function P(n){be=!0,t.isString(e.searchText)||(e.searchText=""),le.hidden=W(),le.hidden||se()}function F(t){switch(t.keyCode){case s.KEY_CODE.DOWN_ARROW:if(le.loading)return;t.stopPropagation(),t.preventDefault(),le.index=Math.min(le.index+1,le.matches.length-1),ie(),ne();break;case s.KEY_CODE.UP_ARROW:if(le.loading)return;t.stopPropagation(),t.preventDefault(),le.index=le.index<0?le.matches.length-1:Math.max(0,le.index-1),ie(),ne();break;case s.KEY_CODE.TAB:if(w(),le.hidden||le.loading||le.index<0||le.matches.length<1)return;J(le.index);break;case s.KEY_CODE.ENTER:if(le.hidden||le.loading||le.index<0||le.matches.length<1)return;if(K())return;t.stopPropagation(),t.preventDefault(),J(le.index);break;case s.KEY_CODE.ESCAPE:t.stopPropagation(),t.preventDefault(),e.searchText&&ee(),L(!0)}}function B(){return t.isNumber(e.minLength)?e.minLength:1}function U(t){function n(t){return t&&e.itemText?e.itemText(j(t)):null}return p.when(n(t)||t)}function j(e){if(!e)return n;var t={};return le.itemName&&(t[le.itemName]=e),t}function z(){return e.autoselect?0:-1}function q(e){le.loading!=e&&(le.loading=e),le.hidden=W()}function W(){return le.loading&&!Y()?!0:K()?!0:be?!V():!0}function V(){return Q()&&Y()||de()}function Y(){return!!le.matches.length}function K(){return!!le.scope.selectedItem}function G(){return le.loading&&!K()}function X(){return U(le.matches[le.index])}function Q(){return(e.searchText||"").length>=B()}function Z(e,t,n){Object.defineProperty(le,e,{get:function(){return n},set:function(e){var o=n;n=e,t(e,o)}})}function J(t){d.nextTick(function(){U(le.matches[t]).then(function(e){var t=he.$.input.controller("ngModel");t.$setViewValue(e),t.$render()})["finally"](function(){e.selectedItem=le.matches[t],q(!1)})},!1)}function ee(t){q(!0),le.index=0,le.matches=[],e.searchText="";var n=document.createEvent("CustomEvent");n.initCustomEvent("input",!0,!0,{value:""}),he.input.dispatchEvent(n),he.input.blur(),e.searchText="",he.input.focus()}function te(n){function o(t){t&&(t=p.when(t),Ee++,q(!0),d.nextTick(function(){t.then(i)["finally"](function(){0===--Ee&&q(!1)})},!0,e))}function i(t){pe[a]=t,(n||"")===(e.searchText||"")&&(le.matches=t,le.hidden=W(),le.loading&&q(!1),e.selectOnMatch&&ce(),ne(),g())}var r=e.$parent.$eval(ue),a=n.toLowerCase(),s=t.isArray(r),c=!!r.then;s?i(r):c&&o(r)}function ne(){X().then(function(e){le.messages=[oe(),e]})}function oe(){if(ve===le.matches.length)return"";switch(ve=le.matches.length,le.matches.length){case 0:return"There are no matches available.";case 1:return"There is 1 match available.";default:return"There are "+le.matches.length+" matches available."}}function ie(){if(he.li[0]){var e=he.li[0].offsetHeight,t=e*le.index,n=t+e,o=he.scroller.clientHeight,i=he.scroller.scrollTop;i>t?ae(t):n>i+o&&ae(n-o)}}function re(){return 0!==Ee}function ae(e){he.$.scrollContainer.controller("mdVirtualRepeatContainer").scrollTo(e)}function de(){var e=(le.scope.searchText||"").length;return le.hasNotFound&&!Y()&&(!le.loading||re())&&e>=B()&&(be||fe)&&!K()}function se(){var t=e.searchText||"",n=t.toLowerCase();!e.noCache&&pe[n]?(le.matches=pe[n],ne(),q(!1)):te(t),le.hidden=W()}function ce(){var t=e.searchText,n=le.matches,o=n[0];1===n.length&&U(o).then(function(n){var o=t==n;e.matchInsensitive&&!o&&(o=t.toLowerCase()==n.toLowerCase()),o&&J(0)})}var le=this,me=e.itemsExpr.split(/ in /i),ue=me[1],he=null,pe={},fe=!1,ge=[],be=!1,ve=0,Ee=0,$e=null;return Z("hidden",_,!0),le.scope=e,le.parent=e.$parent,le.itemName=me[0],le.matches=[],le.loading=!1,le.hidden=!0,le.index=null,le.messages=[],le.id=d.nextUid(),le.isDisabled=null,le.isRequired=null,le.isReadonly=null,le.hasNotFound=!1,le.keydown=F,le.blur=R,le.focus=P,le.clear=ee,le.select=J,le.listEnter=T,le.listLeave=w,le.mouseUp=k,le.getCurrentDisplayValue=X,le.registerSelectedItemWatcher=H,le.unregisterSelectedItemWatcher=I,le.notFoundVisible=de,le.loadingIsVisible=G,f()}t.module("material.components.autocomplete").controller("MdAutocompleteCtrl",e);var o=41,i=5.5*o,r=8,a=2;e.$inject=["$scope","$element","$mdUtil","$mdConstant","$mdTheming","$window","$animate","$rootElement","$attrs","$q"]}(),function(){function e(e){return{controller:"MdAutocompleteCtrl",controllerAs:"$mdAutocompleteCtrl",scope:{inputName:"@mdInputName",inputMinlength:"@mdInputMinlength",inputMaxlength:"@mdInputMaxlength",searchText:"=?mdSearchText",selectedItem:"=?mdSelectedItem",itemsExpr:"@mdItems",itemText:"&mdItemText",placeholder:"@placeholder",noCache:"=?mdNoCache",selectOnMatch:"=?mdSelectOnMatch",matchInsensitive:"=?mdMatchCaseInsensitive",itemChange:"&?mdSelectedItemChange",textChange:"&?mdSearchTextChange",minLength:"=?mdMinLength",delay:"=?mdDelay",autofocus:"=?mdAutofocus",floatingLabel:"@?mdFloatingLabel",autoselect:"=?mdAutoselect",menuClass:"@?mdMenuClass",inputId:"@?mdInputId"},link:function(e,t,n,o){o.hasNotFound=!!t.attr("md-has-not-found")},template:function(t,n){function o(){var e=t.find("md-item-template").detach(),n=e.length?e.html():t.html();return e.length||t.empty(),"<md-autocomplete-parent-scope md-autocomplete-replace>"+n+"</md-autocomplete-parent-scope>"}function i(){var e=t.find("md-not-found").detach(),n=e.length?e.html():"";return n?'<li ng-if="$mdAutocompleteCtrl.notFoundVisible()" md-autocomplete-parent-scope>'+n+"</li>":""}function r(){return n.mdFloatingLabel?' <md-input-container flex ng-if="floatingLabel"> <label>{{floatingLabel}}</label> <input type="search" '+(null!=c?'tabindex="'+c+'"':"")+' id="{{ inputId || \'fl-input-\' + $mdAutocompleteCtrl.id }}" name="{{inputName}}" autocomplete="off" ng-required="$mdAutocompleteCtrl.isRequired" ng-readonly="$mdAutocompleteCtrl.isReadonly" ng-minlength="inputMinlength" ng-maxlength="inputMaxlength" ng-disabled="$mdAutocompleteCtrl.isDisabled" ng-model="$mdAutocompleteCtrl.scope.searchText" ng-keydown="$mdAutocompleteCtrl.keydown($event)" ng-blur="$mdAutocompleteCtrl.blur()" '+(null!=n.mdNoAsterisk?'md-no-asterisk="'+n.mdNoAsterisk+'"':"")+' ng-focus="$mdAutocompleteCtrl.focus($event)" aria-owns="ul-{{$mdAutocompleteCtrl.id}}" '+(null!=n.mdSelectOnFocus?'md-select-on-focus=""':"")+' aria-label="{{floatingLabel}}" aria-autocomplete="list" role="combobox" aria-haspopup="true" aria-activedescendant="" aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/> <div md-autocomplete-parent-scope md-autocomplete-replace>'+s+"</div> </md-input-container>":' <input flex type="search" '+(null!=c?'tabindex="'+c+'"':"")+' id="{{ inputId || \'input-\' + $mdAutocompleteCtrl.id }}" name="{{inputName}}" ng-if="!floatingLabel" autocomplete="off" ng-required="$mdAutocompleteCtrl.isRequired" ng-disabled="$mdAutocompleteCtrl.isDisabled" ng-readonly="$mdAutocompleteCtrl.isReadonly" ng-model="$mdAutocompleteCtrl.scope.searchText" ng-keydown="$mdAutocompleteCtrl.keydown($event)" ng-blur="$mdAutocompleteCtrl.blur()" ng-focus="$mdAutocompleteCtrl.focus($event)" placeholder="{{placeholder}}" aria-owns="ul-{{$mdAutocompleteCtrl.id}}" '+(null!=n.mdSelectOnFocus?'md-select-on-focus=""':"")+' aria-label="{{placeholder}}" aria-autocomplete="list" role="combobox" aria-haspopup="true" aria-activedescendant="" aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/> <button type="button" tabindex="-1" ng-if="$mdAutocompleteCtrl.scope.searchText && !$mdAutocompleteCtrl.isDisabled" ng-click="$mdAutocompleteCtrl.clear($event)"> <md-icon md-svg-src="'+e.mdClose+'"></md-icon> <span class="_md-visually-hidden">Clear</span> </button> '}var a=i(),d=o(),s=t.html(),c=n.tabindex;return a&&t.attr("md-has-not-found",!0),t.attr("tabindex","-1")," <md-autocomplete-wrap layout=\"row\" ng-class=\"{ 'md-whiteframe-z1': !floatingLabel, 'md-menu-showing': !$mdAutocompleteCtrl.hidden }\"> "+r()+' <md-progress-linear class="'+(n.mdFloatingLabel?"md-inline":"")+'" ng-if="$mdAutocompleteCtrl.loadingIsVisible()" md-mode="indeterminate"></md-progress-linear> <md-virtual-repeat-container md-auto-shrink md-auto-shrink-min="1" ng-mouseenter="$mdAutocompleteCtrl.listEnter()" ng-mouseleave="$mdAutocompleteCtrl.listLeave()" ng-mouseup="$mdAutocompleteCtrl.mouseUp()" ng-hide="$mdAutocompleteCtrl.hidden" class="md-autocomplete-suggestions-container md-whiteframe-z1" ng-class="{ \'md-not-found\': $mdAutocompleteCtrl.notFoundVisible() }" role="presentation"> <ul class="md-autocomplete-suggestions" ng-class="::menuClass" id="ul-{{$mdAutocompleteCtrl.id}}"> <li md-virtual-repeat="item in $mdAutocompleteCtrl.matches" ng-class="{ selected: $index === $mdAutocompleteCtrl.index }" ng-click="$mdAutocompleteCtrl.select($index)" md-extra-name="$mdAutocompleteCtrl.itemName"> '+d+" </li>"+a+' </ul> </md-virtual-repeat-container> </md-autocomplete-wrap> <aria-status class="_md-visually-hidden" role="status" aria-live="assertive"> <p ng-repeat="message in $mdAutocompleteCtrl.messages track by $index" ng-if="message">{{message}}</p> </aria-status>'}}}t.module("material.components.autocomplete").directive("mdAutocomplete",e),e.$inject=["$$mdSvgRegistry"]}(),function(){function e(e,t){function n(e,n,o){return function(e,n,i){function r(n,o){s[o]=e[n],e.$watch(n,function(e){t.nextTick(function(){s[o]=e})})}function a(){var t=!1,n=!1;e.$watch(function(){n||t||(t=!0,e.$$postDigest(function(){n||s.$digest(),t=n=!1}))}),s.$watch(function(){n=!0})}var d=e.$mdAutocompleteCtrl,s=d.parent.$new(),c=d.itemName;r("$index","$index"),r("item",c),a(),o(s,function(e){n.after(e)})}}return{restrict:"AE",compile:n,terminal:!0,transclude:"element"}}t.module("material.components.autocomplete").directive("mdAutocompleteParentScope",e),e.$inject=["$compile","$mdUtil"]}(),function(){function e(e,n,o){function i(i,r){var d=null,s=null,c=o.mdHighlightFlags||"",l=e.$watch(function(e){return{term:i(e),unsafeText:r(e)}},function(e,o){null!==d&&e.unsafeText===o.unsafeText||(d=t.element("<div>").text(e.unsafeText).html()),null!==s&&e.term===o.term||(s=a(e.term,c)),n.html(d.replace(s,'<span class="highlight">$&</span>'))},!0);n.on("$destroy",l)}function r(e){return e&&e.replace(/[\\\^\$\*\+\?\.\(\)\|\{}\[\]]/g,"\\$&")}function a(e,t){var n="",o="";return t.indexOf("^")>=0&&(n="^"),t.indexOf("$")>=0&&(o="$"),new RegExp(n+r(e)+o,t.replace(/[\$\^]/g,""))}this.init=i}t.module("material.components.autocomplete").controller("MdHighlightCtrl",e),e.$inject=["$scope","$element","$attrs"]}(),function(){function e(e,t){return{terminal:!0,controller:"MdHighlightCtrl",compile:function(n,o){var i=t(o.mdHighlightText),r=e(n.html());return function(e,t,n,o){o.init(i,r)}}}}t.module("material.components.autocomplete").directive("mdHighlightText",e),e.$inject=["$interpolate","$parse"]}(),function(){function o(e,t,o,i,r){this.$scope=e,this.$element=t,this.$mdConstant=o,this.$timeout=i,this.$mdUtil=r,this.isEditting=!1,this.parentController=n,this.enableChipEdit=!1}t.module("material.components.chips").controller("MdChipCtrl",o),o.$inject=["$scope","$element","$mdConstant","$timeout","$mdUtil"],o.prototype.init=function(e){this.parentController=e,this.enableChipEdit=this.parentController.enableChipEdit,this.enableChipEdit&&(this.$element.on("keydown",this.chipKeyDown.bind(this)),this.$element.on("mousedown",this.chipMouseDown.bind(this)),this.getChipContent().addClass("_md-chip-content-edit-is-enabled"))},o.prototype.getChipContent=function(){var e=this.$element[0].getElementsByClassName("_md-chip-content");return t.element(e[0])},o.prototype.getContentElement=function(){return t.element(this.getChipContent().children()[0])},o.prototype.getChipIndex=function(){return parseInt(this.$element.attr("index"))},o.prototype.goOutOfEditMode=function(){if(this.isEditting){this.isEditting=!1,this.$element.removeClass("_md-chip-editing"),this.getChipContent()[0].contentEditable="false";var e=this.getChipIndex(),t=this.getContentElement().text();t?(this.parentController.updateChipContents(e,this.getContentElement().text()),this.$mdUtil.nextTick(function(){this.parentController.selectedChip===e&&this.parentController.focusChip(e)}.bind(this))):this.parentController.removeChipAndFocusInput(e)}},o.prototype.selectNodeContents=function(t){var n,o;document.body.createTextRange?(n=document.body.createTextRange(),n.moveToElementText(t),n.select()):e.getSelection&&(o=e.getSelection(),n=document.createRange(),n.selectNodeContents(t),o.removeAllRanges(),o.addRange(n))},o.prototype.goInEditMode=function(){this.isEditting=!0,this.$element.addClass("_md-chip-editing"),this.getChipContent()[0].contentEditable="true",this.getChipContent().on("blur",function(){this.goOutOfEditMode()}.bind(this)),this.selectNodeContents(this.getChipContent()[0])},o.prototype.chipKeyDown=function(e){this.isEditting||e.keyCode!==this.$mdConstant.KEY_CODE.ENTER&&e.keyCode!==this.$mdConstant.KEY_CODE.SPACE?this.isEditting&&e.keyCode===this.$mdConstant.KEY_CODE.ENTER&&(e.preventDefault(),this.goOutOfEditMode()):(e.preventDefault(),this.goInEditMode())},o.prototype.chipMouseDown=function(){this.getChipIndex()==this.parentController.selectedChip&&this.enableChipEdit&&!this.isEditting&&this.goInEditMode()}}(),function(){function e(e,o){function i(n,i){return n.append(o.processTemplate(r)),function(n,o,i,r){var a=r.shift(),d=r.shift();e(o),a&&(d.init(a),t.element(o[0].querySelector("._md-chip-content")).on("blur",function(){a.resetSelectedChip(),a.$scope.$applyAsync()}))}}var r=o.processTemplate(n);return{restrict:"E",require:["^?mdChips","mdChip"],compile:i,controller:"MdChipCtrl"}}t.module("material.components.chips").directive("mdChip",e);var n=' <span ng-if="!$mdChipsCtrl.readonly" class="_md-visually-hidden"> {{$mdChipsCtrl.deleteHint}} </span>';e.$inject=["$mdTheming","$mdUtil"]}(),function(){function e(e){function t(t,n,o,i){n.on("click",function(e){t.$apply(function(){i.removeChip(t.$$replacedScope.$index)})}),e(function(){n.attr({tabindex:-1,"aria-hidden":!0}),n.find("button").attr("tabindex","-1")})}return{restrict:"A",require:"^mdChips",scope:!1,link:t}}t.module("material.components.chips").directive("mdChipRemove",e),e.$inject=["$timeout"]}(),function(){function e(e){function t(t,n,o){var i=t.$parent.$mdChipsCtrl,r=i.parent.$new(!1,i.parent);r.$$replacedScope=t,r.$chip=t.$chip,r.$index=t.$index,r.$mdChipsCtrl=i;var a=i.$scope.$eval(o.mdChipTransclude);n.html(a),e(n.contents())(r)}return{restrict:"EA",terminal:!0,link:t,scope:!1}}t.module("material.components.chips").directive("mdChipTransclude",e),e.$inject=["$compile"]}(),function(){function e(e,t,n,o,i,r){this.$timeout=i,this.$mdConstant=t,this.$scope=e,this.parent=e.$parent,this.$log=n,this.$element=o,this.ngModelCtrl=null,this.userInputNgModelCtrl=null,this.userInputElement=null,this.items=[],this.selectedChip=-1,this.hasAutocomplete=!1,this.enableChipEdit=r.parseAttributeBoolean(this.mdEnableChipEdit),this.deleteHint="Press delete to remove this chip.",this.deleteButtonLabel="Remove",this.chipBuffer="",this.useTransformChip=!1,this.useOnAdd=!1,this.useOnRemove=!1,this.useOnSelect=!1}t.module("material.components.chips").controller("MdChipsCtrl",e),e.$inject=["$scope","$mdConstant","$log","$element","$timeout","$mdUtil"],e.prototype.inputKeydown=function(e){var t=this.getChipBuffer();if(!(this.hasAutocomplete&&e.isDefaultPrevented&&e.isDefaultPrevented())){if(e.keyCode===this.$mdConstant.KEY_CODE.BACKSPACE){if(t)return;return e.preventDefault(),e.stopPropagation(),void(this.items.length&&this.selectAndFocusChipSafe(this.items.length-1))}if((!this.separatorKeys||this.separatorKeys.length<1)&&(this.separatorKeys=[this.$mdConstant.KEY_CODE.ENTER]),-1!==this.separatorKeys.indexOf(e.keyCode)){if(this.hasAutocomplete&&this.requireMatch||!t)return;if(e.preventDefault(),this.hasMaxChipsReached())return;this.appendChip(t.trim()),this.resetChipBuffer()}}},e.prototype.updateChipContents=function(e,t){e>=0&&e<this.items.length&&(this.items[e]=t,this.ngModelCtrl.$setDirty())},e.prototype.isEditingChip=function(){return!!this.$element[0].getElementsByClassName("_md-chip-editing").length},e.prototype.chipKeydown=function(e){if(!this.getChipBuffer()&&!this.isEditingChip())switch(e.keyCode){case this.$mdConstant.KEY_CODE.BACKSPACE:case this.$mdConstant.KEY_CODE.DELETE:if(this.selectedChip<0)return;e.preventDefault(),this.removeAndSelectAdjacentChip(this.selectedChip);break;case this.$mdConstant.KEY_CODE.LEFT_ARROW:e.preventDefault(),this.selectedChip<0&&(this.selectedChip=this.items.length),this.items.length&&this.selectAndFocusChipSafe(this.selectedChip-1);break;case this.$mdConstant.KEY_CODE.RIGHT_ARROW:e.preventDefault(),this.selectAndFocusChipSafe(this.selectedChip+1);break;case this.$mdConstant.KEY_CODE.ESCAPE:case this.$mdConstant.KEY_CODE.TAB:if(this.selectedChip<0)return;e.preventDefault(),this.onFocus()}},e.prototype.getPlaceholder=function(){var e=this.items&&this.items.length&&(""==this.secondaryPlaceholder||this.secondaryPlaceholder);return e?this.secondaryPlaceholder:this.placeholder},e.prototype.removeAndSelectAdjacentChip=function(e){var n=this.getAdjacentChipIndex(e);this.removeChip(e),this.$timeout(t.bind(this,function(){this.selectAndFocusChipSafe(n)}))},e.prototype.resetSelectedChip=function(){this.selectedChip=-1},e.prototype.getAdjacentChipIndex=function(e){var t=this.items.length-1;return 0==t?-1:e==t?e-1:e},e.prototype.appendChip=function(e){if(this.useTransformChip&&this.transformChip){var n=this.transformChip({$chip:e});t.isDefined(n)&&(e=n)}if(t.isObject(e)){var o=this.items.some(function(n){return t.equals(e,n)});if(o)return}if(!(null==e||this.items.indexOf(e)+1)){var i=this.items.push(e);this.ngModelCtrl.$setDirty(),this.validateModel(),this.useOnAdd&&this.onAdd&&this.onAdd({$chip:e,$index:i})}},e.prototype.useTransformChipExpression=function(){this.useTransformChip=!0},e.prototype.useOnAddExpression=function(){this.useOnAdd=!0},e.prototype.useOnRemoveExpression=function(){this.useOnRemove=!0},e.prototype.useOnSelectExpression=function(){this.useOnSelect=!0},e.prototype.getChipBuffer=function(){return this.userInputElement?this.userInputNgModelCtrl?this.userInputNgModelCtrl.$viewValue:this.userInputElement[0].value:this.chipBuffer},e.prototype.resetChipBuffer=function(){this.userInputElement?this.userInputNgModelCtrl?(this.userInputNgModelCtrl.$setViewValue(""),this.userInputNgModelCtrl.$render()):this.userInputElement[0].value="":this.chipBuffer=""},e.prototype.hasMaxChipsReached=function(){return t.isString(this.maxChips)&&(this.maxChips=parseInt(this.maxChips,10)||0),this.maxChips>0&&this.items.length>=this.maxChips},e.prototype.validateModel=function(){this.ngModelCtrl.$setValidity("md-max-chips",!this.hasMaxChipsReached())},e.prototype.removeChip=function(e){var t=this.items.splice(e,1);this.ngModelCtrl.$setDirty(),this.validateModel(),t&&t.length&&this.useOnRemove&&this.onRemove&&this.onRemove({$chip:t[0],$index:e})},e.prototype.removeChipAndFocusInput=function(e){this.removeChip(e),this.onFocus()},e.prototype.selectAndFocusChipSafe=function(e){return this.items.length?e===this.items.length?this.onFocus():(e=Math.max(e,0),e=Math.min(e,this.items.length-1),this.selectChip(e),void this.focusChip(e)):(this.selectChip(-1),void this.onFocus())},e.prototype.selectChip=function(e){e>=-1&&e<=this.items.length?(this.selectedChip=e,this.useOnSelect&&this.onSelect&&this.onSelect({$chip:this.items[this.selectedChip]})):this.$log.warn("Selected Chip index out of bounds; ignoring.")},e.prototype.selectAndFocusChip=function(e){this.selectChip(e),-1!=e&&this.focusChip(e)},e.prototype.focusChip=function(e){this.$element[0].querySelector('md-chip[index="'+e+'"] ._md-chip-content').focus()},e.prototype.configureNgModel=function(e){this.ngModelCtrl=e;var t=this;e.$render=function(){t.items=t.ngModelCtrl.$viewValue}},e.prototype.onFocus=function(){var e=this.$element[0].querySelector("input");e&&e.focus(),this.resetSelectedChip()},e.prototype.onInputFocus=function(){this.inputHasFocus=!0,this.resetSelectedChip()},e.prototype.onInputBlur=function(){this.inputHasFocus=!1},e.prototype.configureUserInput=function(e){this.userInputElement=e;var n=e.controller("ngModel");n!=this.ngModelCtrl&&(this.userInputNgModelCtrl=n);var o=this.$scope,i=this,r=function(e,n){o.$evalAsync(t.bind(i,n,e))};e.attr({tabindex:0}).on("keydown",function(e){r(e,i.inputKeydown)}).on("focus",function(e){r(e,i.onInputFocus)}).on("blur",function(e){r(e,i.onInputBlur)})},e.prototype.configureAutocomplete=function(e){e&&(this.hasAutocomplete=!0,e.registerSelectedItemWatcher(t.bind(this,function(e){if(e){if(this.hasMaxChipsReached())return;this.appendChip(e),this.resetChipBuffer()}})),this.$element.find("input").on("focus",t.bind(this,this.onInputFocus)).on("blur",t.bind(this,this.onInputBlur)))},e.prototype.hasFocus=function(){return this.inputHasFocus||this.selectedChip>=0}}(),function(){function e(e,t,a,d,s,c){function l(n,o){function i(e){if(o.ngModel){var t=r[0].querySelector(e);return t&&t.outerHTML}}var r=o.$mdUserTemplate;o.$mdUserTemplate=null;var l=i("md-chips>md-chip-template"),m=t.prefixer().buildList("md-chip-remove").map(function(e){return"md-chips>*["+e+"]"}).join(","),h=i(m)||u.remove,p=l||u["default"],f=i("md-chips>md-autocomplete")||i("md-chips>input")||u.input,g=r.find("md-chip");return r[0].querySelector("md-chip-template>*[md-chip-remove]")&&d.warn("invalid placement of md-chip-remove within md-chip-template."),function(n,i,r,d){t.initOptionalProperties(n,o),e(i);var m=d[0];if(l&&(m.enableChipEdit=!1),m.chipContentsTemplate=p,m.chipRemoveTemplate=h,m.chipInputTemplate=f,m.mdCloseIcon=c.mdClose,i.attr({"aria-hidden":!0,tabindex:-1}).on("focus",function(){m.onFocus()}),o.ngModel&&(m.configureNgModel(i.controller("ngModel")),r.mdTransformChip&&m.useTransformChipExpression(),r.mdOnAppend&&m.useOnAppendExpression(),r.mdOnAdd&&m.useOnAddExpression(),r.mdOnRemove&&m.useOnRemoveExpression(),r.mdOnSelect&&m.useOnSelectExpression(),f!=u.input&&n.$watch("$mdChipsCtrl.readonly",function(e){e||t.nextTick(function(){0===f.indexOf("<md-autocomplete")&&m.configureAutocomplete(i.find("md-autocomplete").controller("mdAutocomplete")),m.configureUserInput(i.find("input"))})}),t.nextTick(function(){var e=i.find("input");e&&e.toggleClass("md-input",!0)})),g.length>0){var b=a(g.clone())(n.$parent);s(function(){i.find("md-chips-wrap").prepend(b)})}}}function m(){return{chips:t.processTemplate(n),input:t.processTemplate(o),"default":t.processTemplate(i),remove:t.processTemplate(r)}}var u=m();return{template:function(e,t){return t.$mdUserTemplate=e.clone(),u.chips},require:["mdChips"],restrict:"E",controller:"MdChipsCtrl",controllerAs:"$mdChipsCtrl",bindToController:!0,compile:l,scope:{readonly:"=readonly",placeholder:"@",mdEnableChipEdit:"@",secondaryPlaceholder:"@",maxChips:"@mdMaxChips",transformChip:"&mdTransformChip",onAppend:"&mdOnAppend",onAdd:"&mdOnAdd",onRemove:"&mdOnRemove",onSelect:"&mdOnSelect",deleteHint:"@",deleteButtonLabel:"@",separatorKeys:"=?mdSeparatorKeys",requireMatch:"=?mdRequireMatch"}}}t.module("material.components.chips").directive("mdChips",e);var n=' <md-chips-wrap ng-keydown="$mdChipsCtrl.chipKeydown($event)" ng-class="{ \'md-focused\': $mdChipsCtrl.hasFocus(), \'md-readonly\': !$mdChipsCtrl.ngModelCtrl || $mdChipsCtrl.readonly}" class="md-chips"> <md-chip ng-repeat="$chip in $mdChipsCtrl.items" index="{{$index}}" ng-class="{\'md-focused\': $mdChipsCtrl.selectedChip == $index, \'md-readonly\': !$mdChipsCtrl.ngModelCtrl || $mdChipsCtrl.readonly}"> <div class="_md-chip-content" tabindex="-1" aria-hidden="true" ng-click="!$mdChipsCtrl.readonly && $mdChipsCtrl.focusChip($index)" ng-focus="!$mdChipsCtrl.readonly && $mdChipsCtrl.selectChip($index)" md-chip-transclude="$mdChipsCtrl.chipContentsTemplate"></div> <div ng-if="!$mdChipsCtrl.readonly" class="_md-chip-remove-container" md-chip-transclude="$mdChipsCtrl.chipRemoveTemplate"></div> </md-chip> <div class="_md-chip-input-container"> <div ng-if="!$mdChipsCtrl.readonly && $mdChipsCtrl.ngModelCtrl" md-chip-transclude="$mdChipsCtrl.chipInputTemplate"></div> </div> </md-chips-wrap>',o=' <input class="md-input" tabindex="0" placeholder="{{$mdChipsCtrl.getPlaceholder()}}" aria-label="{{$mdChipsCtrl.getPlaceholder()}}" ng-model="$mdChipsCtrl.chipBuffer" ng-focus="$mdChipsCtrl.onInputFocus()" ng-blur="$mdChipsCtrl.onInputBlur()" ng-trim="false" ng-keydown="$mdChipsCtrl.inputKeydown($event)">',i=" <span>{{$chip}}</span>",r=' <button class="_md-chip-remove" ng-if="!$mdChipsCtrl.readonly" ng-click="$mdChipsCtrl.removeChipAndFocusInput($$replacedScope.$index)" type="button" aria-hidden="true" tabindex="-1"> <md-icon md-svg-src="{{ $mdChipsCtrl.mdCloseIcon }}"></md-icon> <span class="_md-visually-hidden"> {{$mdChipsCtrl.deleteButtonLabel}} </span> </button>';e.$inject=["$mdTheming","$mdUtil","$compile","$log","$timeout","$$mdSvgRegistry"]}(),function(){function e(){this.selectedItem=null,this.searchText=""}t.module("material.components.chips").controller("MdContactChipsCtrl",e),e.prototype.queryContact=function(e){var n=this.contactQuery({$query:e});return this.filterSelected?n.filter(t.bind(this,this.filterSelectedContacts)):n},e.prototype.itemName=function(e){return e[this.contactName]},e.prototype.filterSelectedContacts=function(e){return-1==this.contacts.indexOf(e)}}(),function(){function e(e,t){function o(n,o){return function(n,i,r,a){t.initOptionalProperties(n,o),e(i),i.attr("tabindex","-1")}}return{template:function(e,t){return n},restrict:"E",controller:"MdContactChipsCtrl",controllerAs:"$mdContactChipsCtrl",bindToController:!0,compile:o,scope:{contactQuery:"&mdContacts",placeholder:"@",secondaryPlaceholder:"@",contactName:"@mdContactName",contactImage:"@mdContactImage",contactEmail:"@mdContactEmail",contacts:"=ngModel",requireMatch:"=?mdRequireMatch",highlightFlags:"@?mdHighlightFlags"}}}t.module("material.components.chips").directive("mdContactChips",e);var n=' <md-chips class="md-contact-chips" ng-model="$mdContactChipsCtrl.contacts" md-require-match="$mdContactChipsCtrl.requireMatch" md-autocomplete-snap> <md-autocomplete md-menu-class="md-contact-chips-suggestions" md-selected-item="$mdContactChipsCtrl.selectedItem" md-search-text="$mdContactChipsCtrl.searchText" md-items="item in $mdContactChipsCtrl.queryContact($mdContactChipsCtrl.searchText)" md-item-text="$mdContactChipsCtrl.itemName(item)" md-no-cache="true" md-autoselect placeholder="{{$mdContactChipsCtrl.contacts.length == 0 ? $mdContactChipsCtrl.placeholder : $mdContactChipsCtrl.secondaryPlaceholder}}"> <div class="md-contact-suggestion"> <img ng-src="{{item[$mdContactChipsCtrl.contactImage]}}" alt="{{item[$mdContactChipsCtrl.contactName]}}" ng-if="item[$mdContactChipsCtrl.contactImage]" /> <span class="md-contact-name" md-highlight-text="$mdContactChipsCtrl.searchText" md-highlight-flags="{{$mdContactChipsCtrl.highlightFlags}}"> {{item[$mdContactChipsCtrl.contactName]}} </span> <span class="md-contact-email" >{{item[$mdContactChipsCtrl.contactEmail]}}</span> </div> </md-autocomplete> <md-chip-template> <div class="md-contact-avatar"> <img ng-src="{{$chip[$mdContactChipsCtrl.contactImage]}}" alt="{{$chip[$mdContactChipsCtrl.contactName]}}" ng-if="$chip[$mdContactChipsCtrl.contactImage]" /> </div> <div class="md-contact-name"> {{$chip[$mdContactChipsCtrl.contactName]}} </div> </md-chip-template> </md-chips>';e.$inject=["$mdTheming","$mdUtil"]}(),function(){function e(e,t,n,o){function i(i,a,d){function s(){var e=a.parent();return e.attr("aria-label")||e.text()?!0:!(!e.parent().attr("aria-label")&&!e.parent().text())}function c(){d.mdSvgIcon||d.mdSvgSrc||(d.mdFontIcon&&a.addClass("md-font "+d.mdFontIcon),a.addClass(e.fontSet(d.mdFontSet)))}t(a),c();var l=a[0].getAttribute(d.$attr.mdSvgSrc),m=d.alt||d.mdFontIcon||d.mdSvgIcon||a.text(),u=d.$normalize(d.$attr.mdSvgIcon||d.$attr.mdSvgSrc||"");d["aria-label"]||(""===m||s()?a.text()||n.expect(a,"aria-hidden","true"):(n.expect(a,"aria-label",m),n.expect(a,"role","img"))),u&&d.$observe(u,function(t){r(t)||t!==l||(t=o.trustAsUrl(t)),a.empty(),t&&e(t).then(function(e){a.empty(),a.append(e)})})}function r(e){var t=/^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i;return t.test(e)}return{restrict:"E",link:i}}t.module("material.components.icon").directive("mdIcon",["$mdIcon","$mdTheming","$mdAria","$sce",e])}(),function(){function n(){}function o(e,t){this.url=e,this.viewBoxSize=t||r.defaultViewBoxSize}function i(n,o,i,r,a,d){function s(e){if(e=e||"",t.isString(e)||(e=d.getTrustedUrl(e)),E[e])return i.when(l(E[e]));if($.test(e)||C.test(e))return p(e).then(m(e));-1==e.indexOf(":")&&(e="$default:"+e);var o=n[e]?u:h;return o(e).then(m(e))}function c(e){var o=t.isUndefined(e)||!(e&&e.length);if(o)return n.defaultFontSet;var i=e;return t.forEach(n.fontSets,function(t){t.alias==e&&(i=t.fontSet||i)}),i}function l(e){var n=e.clone(),o="_cache"+a.nextUid();return n.id&&(n.id+=o),t.forEach(n.querySelectorAll("[id]"),function(e){e.id+=o}),n}function m(e){return function(t){return E[e]=f(t)?t:new g(t,n[e]),E[e].clone()}}function u(e){var t=n[e];return p(t.url).then(function(e){return new g(e,t)})}function h(e){function t(t){var n=e.slice(e.lastIndexOf(":")+1),i=t.querySelector("#"+n);return i?new g(i,d):o(e)}function o(e){var t="icon "+e+" not found";return r.warn(t),i.reject(t||e)}var a=e.substring(0,e.lastIndexOf(":"))||"$default",d=n[a];return d?p(d.url).then(t):o(e)}function p(n){function a(n){var o=C.exec(n),r=/base64/i.test(n),a=r?e.atob(o[2]):o[2];return i.when(t.element(a)[0])}function d(e){return i(function(n,i){var a=function(e){var n=t.isString(e)?e:e.message||e.data||e.statusText;r.warn(n),i(e)},d=function(e){var o=t.element("<div>").append(e).find("svg")[0];n(o)};o(e,!0).then(d,a)})}return C.test(n)?a(n):d(n)}function f(e){return t.isDefined(e.element)&&t.isDefined(e.config)}function g(e,n){e&&"svg"!=e.tagName&&(e=t.element('<svg xmlns="http://www.w3.org/2000/svg">').append(e)[0]), +e.getAttribute("xmlns")||e.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.element=e,this.config=n,this.prepare()}function b(){var e=this.config?this.config.viewBoxSize:n.defaultViewBoxSize;t.forEach({fit:"",height:"100%",width:"100%",preserveAspectRatio:"xMidYMid meet",viewBox:this.element.getAttribute("viewBox")||"0 0 "+e+" "+e,focusable:!1},function(e,t){this.element.setAttribute(t,e)},this)}function v(){return this.element.cloneNode(!0)}var E={},$=/[-\w@:%\+.~#?&\/\/=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&\/\/=]*)?/i,C=/^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i;return g.prototype={clone:v,prepare:b},s.fontSet=c,s}t.module("material.components.icon").constant("$$mdSvgRegistry",{mdTabsArrow:"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyICIvPjwvZz48L3N2Zz4=",mdClose:"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xOSA2LjQxbC0xLjQxLTEuNDEtNS41OSA1LjU5LTUuNTktNS41OS0xLjQxIDEuNDEgNS41OSA1LjU5LTUuNTkgNS41OSAxLjQxIDEuNDEgNS41OS01LjU5IDUuNTkgNS41OSAxLjQxLTEuNDEtNS41OS01LjU5eiIvPjwvZz48L3N2Zz4=",mdCancel:"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xMiAyYy01LjUzIDAtMTAgNC40Ny0xMCAxMHM0LjQ3IDEwIDEwIDEwIDEwLTQuNDcgMTAtMTAtNC40Ny0xMC0xMC0xMHptNSAxMy41OWwtMS40MSAxLjQxLTMuNTktMy41OS0zLjU5IDMuNTktMS40MS0xLjQxIDMuNTktMy41OS0zLjU5LTMuNTkgMS40MS0xLjQxIDMuNTkgMy41OSAzLjU5LTMuNTkgMS40MSAxLjQxLTMuNTkgMy41OSAzLjU5IDMuNTl6Ii8+PC9nPjwvc3ZnPg==",mdMenu:"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0zLDZIMjFWOEgzVjZNMywxMUgyMVYxM0gzVjExTTMsMTZIMjFWMThIM1YxNloiIC8+PC9zdmc+",mdToggleArrow:"data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDggNDgiPjxwYXRoIGQ9Ik0yNCAxNmwtMTIgMTIgMi44MyAyLjgzIDkuMTctOS4xNyA5LjE3IDkuMTcgMi44My0yLjgzeiIvPjxwYXRoIGQ9Ik0wIDBoNDh2NDhoLTQ4eiIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==",mdCalendar:"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgM2gtMVYxaC0ydjJIOFYxSDZ2Mkg1Yy0xLjExIDAtMS45OS45LTEuOTkgMkwzIDE5YzAgMS4xLjg5IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjVjMC0xLjEtLjktMi0yLTJ6bTAgMTZINVY4aDE0djExek03IDEwaDV2NUg3eiIvPjwvc3ZnPg=="}).provider("$mdIcon",n);var r={defaultViewBoxSize:24,defaultFontSet:"material-icons",fontSets:[]};n.prototype={icon:function(e,t,n){return-1==e.indexOf(":")&&(e="$default:"+e),r[e]=new o(t,n),this},iconSet:function(e,t,n){return r[e]=new o(t,n),this},defaultIconSet:function(e,t){var n="$default";return r[n]||(r[n]=new o(e,t)),r[n].viewBoxSize=t||r.defaultViewBoxSize,this},defaultViewBoxSize:function(e){return r.defaultViewBoxSize=e,this},fontSet:function(e,t){return r.fontSets.push({alias:e,fontSet:t||e}),this},defaultFontSet:function(e){return r.defaultFontSet=e?e:"",this},defaultIconSize:function(e){return r.defaultIconSize=e,this},$get:["$templateRequest","$q","$log","$templateCache","$mdUtil","$sce",function(e,t,n,o,a,d){return i(r,e,t,n,a,d)}]},i.$inject=["config","$templateRequest","$q","$log","$mdUtil","$sce"]}(),function(){function e(e,o,i,r,a,d,s,c){var l,m,u=a.prefixer(),h=this;this.nestLevel=parseInt(o.mdNestLevel,10)||0,this.init=function(n,o){o=o||{},l=n,m=i[0].querySelector(u.buildSelector(["ng-click","ng-mouseenter"])),m.setAttribute("aria-expanded","false"),this.isInMenuBar=o.isInMenuBar,this.nestedMenus=a.nodesToArray(l[0].querySelectorAll(".md-nested-menu")),l.on("$mdInterimElementRemove",function(){h.isOpen=!1,a.nextTick(function(){h.onIsOpenChanged(h.isOpen)})}),a.nextTick(function(){h.onIsOpenChanged(h.isOpen)});var d="menu_container_"+a.nextUid();l.attr("id",d),t.element(m).attr({"aria-owns":d,"aria-haspopup":"true"}),r.$on("$destroy",this.disableHoverListener),l.on("$destroy",function(){e.destroy()})};var p,f,g=[];this.enableHoverListener=function(){g.push(s.$on("$mdMenuOpen",function(e,t){l[0].contains(t[0])&&(h.currentlyOpenMenu=t.controller("mdMenu"),h.isAlreadyOpening=!1,h.currentlyOpenMenu.registerContainerProxy(h.triggerContainerProxy.bind(h)))})),g.push(s.$on("$mdMenuClose",function(e,t){l[0].contains(t[0])&&(h.currentlyOpenMenu=n)})),f=t.element(a.nodesToArray(l[0].children[0].children)),f.on("mouseenter",h.handleMenuItemHover),f.on("mouseleave",h.handleMenuItemMouseLeave)},this.disableHoverListener=function(){for(;g.length;)g.shift()();f&&f.off("mouseenter",h.handleMenuItemHover),f&&f.off("mouseleave",h.handleMenuItemMouseLeave)},this.handleMenuItemHover=function(e){if(!h.isAlreadyOpening){var n=e.target.querySelector("md-menu")||a.getClosest(e.target,"MD-MENU");p=d(function(){if(n&&(n=t.element(n).controller("mdMenu")),h.currentlyOpenMenu&&h.currentlyOpenMenu!=n){var e=h.nestLevel+1;h.currentlyOpenMenu.close(!0,{closeTo:e}),h.isAlreadyOpening=!!n,n&&n.open()}else n&&!n.isOpen&&n.open&&(h.isAlreadyOpening=!!n,n&&n.open())},n?100:250);var o=e.currentTarget.querySelector(".md-button:not([disabled])");o&&o.focus()}},this.handleMenuItemMouseLeave=function(){p&&(d.cancel(p),p=n)},this.open=function(t){t&&t.stopPropagation(),t&&t.preventDefault(),h.isOpen||(h.enableHoverListener(),h.isOpen=!0,a.nextTick(function(){h.onIsOpenChanged(h.isOpen)}),m=m||(t?t.target:i[0]),m.setAttribute("aria-expanded","true"),r.$emit("$mdMenuOpen",i),e.show({scope:r,mdMenuCtrl:h,nestLevel:h.nestLevel,element:l,target:m,preserveElement:!0,parent:"body"})["finally"](function(){m.setAttribute("aria-expanded","false"),h.disableHoverListener()}))},r.$mdOpenMenu=this.open,this.onIsOpenChanged=function(e){e?(l.attr("aria-hidden","false"),i[0].classList.add("_md-open"),t.forEach(h.nestedMenus,function(e){e.classList.remove("_md-open")})):(l.attr("aria-hidden","true"),i[0].classList.remove("_md-open")),r.$mdMenuIsOpen=h.isOpen},this.focusMenuContainer=function(){var e=l[0].querySelector(u.buildSelector(["md-menu-focus-target","md-autofocus"]));e||(e=l[0].querySelector(".md-button")),e.focus()},this.registerContainerProxy=function(e){this.containerProxy=e},this.triggerContainerProxy=function(e){this.containerProxy&&this.containerProxy(e)},this.destroy=function(){return h.isOpen?e.destroy():c.when(!1)},this.close=function(n,o){if(h.isOpen){h.isOpen=!1,a.nextTick(function(){h.onIsOpenChanged(h.isOpen)});var d=t.extend({},o,{skipFocus:n});if(r.$emit("$mdMenuClose",i,d),e.hide(null,o),!n){var s=h.restoreFocusTo||i.find("button")[0];s instanceof t.element&&(s=s[0]),s&&s.focus()}}},this.positionMode=function(){var e=(o.mdPositionMode||"target").split(" ");return 1==e.length&&e.push(e[0]),{left:e[0],top:e[1]}},this.offsets=function(){var e=(o.mdOffset||"0 0").split(" ").map(parseFloat);if(2==e.length)return{left:e[0],top:e[1]};if(1==e.length)return{top:e[0],left:e[0]};throw Error("Invalid offsets specified. Please follow format <x, y> or <n>")}}t.module("material.components.menu").controller("mdMenuCtrl",e),e.$inject=["$mdMenu","$attrs","$element","$scope","$mdUtil","$timeout","$rootScope","$q"]}(),function(){function e(e){function o(n){n.addClass("md-menu");var o=n.children()[0],a=e.prefixer();if(a.hasAttribute(o,"ng-click")||(o=o.querySelector(a.buildSelector(["ng-click","ng-mouseenter"]))||o),!o||"MD-BUTTON"!=o.nodeName&&"BUTTON"!=o.nodeName||o.hasAttribute("type")||o.setAttribute("type","button"),2!=n.children().length)throw Error(r+"Expected two children elements.");o&&o.setAttribute("aria-haspopup","true");var d=n[0].querySelectorAll("md-menu"),s=parseInt(n[0].getAttribute("md-nest-level"),10)||0;return d&&t.forEach(e.nodesToArray(d),function(e){e.hasAttribute("md-position-mode")||e.setAttribute("md-position-mode","cascade"),e.classList.add("_md-nested-menu"),e.setAttribute("md-nest-level",s+1)}),i}function i(e,o,i,r){var a=r[0],d=r[1]!=n,s=t.element('<div class="_md _md-open-menu-container md-whiteframe-z2"></div>'),c=o.children()[1];o.addClass("_md"),c.hasAttribute("role")||c.setAttribute("role","menu"),s.append(c),o.on("$destroy",function(){s.remove()}),o.append(s),s[0].style.display="none",a.init(s,{isInMenuBar:d})}var r="Invalid HTML for md-menu: ";return{restrict:"E",require:["mdMenu","?^mdMenuBar"],controller:"mdMenuCtrl",scope:!0,compile:o}}t.module("material.components.menu").directive("mdMenu",e),e.$inject=["$mdUtil"]}(),function(){function e(e){function o(e,o,a,d,s,c,l,m,u){function h(n,o,i){return i.nestLevel?t.noop:(i.disableParentScroll&&!e.getClosest(i.target,"MD-DIALOG")?i.restoreScroll=e.disableScrollAround(i.element,i.parent):i.disableParentScroll=!1,i.hasBackdrop&&(i.backdrop=e.createBackdrop(n,"_md-menu-backdrop _md-click-catcher"),u.enter(i.backdrop,d[0].body)),function(){i.backdrop&&i.backdrop.remove(),i.disableParentScroll&&i.restoreScroll()})}function p(e,t,n){function o(){return m(t,{addClass:"_md-leave"}).start()}function i(){t.removeClass("_md-active"),v(t,n),n.alreadyOpen=!1}return n.cleanupInteraction(),n.cleanupResizing(),n.hideBackdrop(),n.$destroy===!0?i():o().then(i)}function f(n,i,r){function d(){return r.parent.append(i),i[0].style.display="",c(function(e){var t=E(i,r);i.removeClass("_md-leave"),m(i,{addClass:"_md-active",from:C.toCss(t),to:C.toCss({transform:""})}).start().then(e)})}function u(){if(!r.target)throw Error("$mdMenu.show() expected a target to animate from in options.target");t.extend(r,{alreadyOpen:!1,isRemoved:!1,target:t.element(r.target),parent:t.element(r.parent),menuContentEl:t.element(i[0].querySelector("md-menu-content"))})}function p(){var e=function(e,t){return l.throttle(function(){if(!r.isRemoved){var n=E(e,t);e.css(C.toCss(n))}})}(i,r);return s.addEventListener("resize",e),s.addEventListener("orientationchange",e),function(){s.removeEventListener("resize",e),s.removeEventListener("orientationchange",e)}}function f(){function t(t){var n;switch(t.keyCode){case a.KEY_CODE.ESCAPE:r.mdMenuCtrl.close(!1,{closeAll:!0}),n=!0;break;case a.KEY_CODE.UP_ARROW:g(t,r.menuContentEl,r,-1)||r.nestLevel||r.mdMenuCtrl.triggerContainerProxy(t),n=!0;break;case a.KEY_CODE.DOWN_ARROW:g(t,r.menuContentEl,r,1)||r.nestLevel||r.mdMenuCtrl.triggerContainerProxy(t),n=!0;break;case a.KEY_CODE.LEFT_ARROW:r.nestLevel?r.mdMenuCtrl.close():r.mdMenuCtrl.triggerContainerProxy(t),n=!0;break;case a.KEY_CODE.RIGHT_ARROW:var o=e.getClosest(t.target,"MD-MENU");o&&o!=r.parent[0]?t.target.click():r.mdMenuCtrl.triggerContainerProxy(t),n=!0}n&&(t.preventDefault(),t.stopImmediatePropagation())}function o(e){e.preventDefault(),e.stopPropagation(),n.$apply(function(){r.mdMenuCtrl.close(!0,{closeAll:!0})})}function d(t){function o(){n.$apply(function(){r.mdMenuCtrl.close(!0,{closeAll:!0})})}function i(e,t){if(!e)return!1;for(var n,o=0;n=t[o];++o)if($.hasAttribute(e,n))return!0;return!1}var a=t.target;do{if(a==r.menuContentEl[0])return;if((i(a,["ng-click","ng-href","ui-sref"])||"BUTTON"==a.nodeName||"MD-BUTTON"==a.nodeName)&&!i(a,["md-prevent-menu-close"])){var d=e.getClosest(a,"MD-MENU");a.hasAttribute("disabled")||d&&d!=r.parent[0]||o();break}}while(a=a.parentNode)}i.addClass("_md-clickable"),r.backdrop&&r.backdrop.on("click",o),r.menuContentEl.on("keydown",t),r.menuContentEl[0].addEventListener("click",d,!0);var s=r.menuContentEl[0].querySelector($.buildSelector(["md-menu-focus-target","md-autofocus"]));if(!s){var c=r.menuContentEl[0].firstElementChild;s=c&&(c.querySelector(".md-button:not([disabled])")||c.firstElementChild)}return s&&s.focus(),function(){i.removeClass("_md-clickable"),r.backdrop&&r.backdrop.off("click",o),r.menuContentEl.off("keydown",t),r.menuContentEl[0].removeEventListener("click",d,!0)}}return u(r),o.inherit(r.menuContentEl,r.target),r.cleanupResizing=p(),r.hideBackdrop=h(n,i,r),d().then(function(e){return r.alreadyOpen=!0,r.cleanupInteraction=f(),e})}function g(t,n,o,i){for(var r,a=e.getClosest(t.target,"MD-MENU-ITEM"),d=e.nodesToArray(n[0].children),s=d.indexOf(a),c=s+i;c>=0&&c<d.length;c+=i){var l=d[c].querySelector(".md-button");if(r=b(l))break}return r}function b(e){return e&&-1!=e.getAttribute("tabindex")?(e.focus(),d[0].activeElement==e):void 0}function v(e,t){t.preserveElement?i(e).style.display="none":i(e).parentNode===i(t.parent)&&i(t.parent).removeChild(i(e))}function E(t,o){function i(e){e.top=Math.max(Math.min(e.top,v.bottom-l.offsetHeight),v.top),e.left=Math.max(Math.min(e.left,v.right-l.offsetWidth),v.left)}function a(){for(var e=0;e<m.children.length;++e)if("none"!=s.getComputedStyle(m.children[e]).display)return m.children[e]}var c,l=t[0],m=t[0].firstElementChild,u=m.getBoundingClientRect(),h=d[0].body,p=h.getBoundingClientRect(),f=s.getComputedStyle(m),g=o.target[0].querySelector($.buildSelector("md-menu-origin"))||o.target[0],b=g.getBoundingClientRect(),v={left:p.left+r,top:Math.max(p.top,0)+r,bottom:Math.max(p.bottom,Math.max(p.top,0)+p.height)-r,right:p.right-r},E={top:0,left:0,right:0,bottom:0},C={top:0,left:0,right:0,bottom:0},y=o.mdMenuCtrl.positionMode();"target"!=y.top&&"target"!=y.left&&"target-right"!=y.left||(c=a(),c&&(c=c.firstElementChild||c,c=c.querySelector($.buildSelector("md-menu-align-target"))||c,E=c.getBoundingClientRect(),C={top:parseFloat(l.style.top||0),left:parseFloat(l.style.left||0)}));var M={},_="top ";switch(y.top){case"target":M.top=C.top+b.top-E.top;break;case"cascade":M.top=b.top-parseFloat(f.paddingTop)-g.style.top;break;case"bottom":M.top=b.top+b.height;break;default:throw new Error('Invalid target mode "'+y.top+'" specified for md-menu on Y axis.')}var A="rtl"==e.bidi();switch(y.left){case"target":M.left=C.left+b.left-E.left,_+=A?"right":"left";break;case"target-left":M.left=b.left,_+="left";break;case"target-right":M.left=b.right-u.width+(u.right-E.right),_+="right";break;case"cascade":var T=A?b.left-u.width<v.left:b.right+u.width<v.right;M.left=T?b.right-g.style.left:b.left-g.style.left-u.width,_+=T?"left":"right";break;case"left":A?(M.left=b.right-u.width,_+="right"):(M.left=b.left,_+="left");break;default:throw new Error('Invalid target mode "'+y.left+'" specified for md-menu on X axis.')}var w=o.mdMenuCtrl.offsets();M.top+=w.top,M.left+=w.left,i(M);var k=Math.round(100*Math.min(b.width/l.offsetWidth,1))/100,x=Math.round(100*Math.min(b.height/l.offsetHeight,1))/100;return{top:Math.round(M.top),left:Math.round(M.left),transform:o.alreadyOpen?n:e.supplant("scale({0},{1})",[k,x]),transformOrigin:_}}var $=e.prefixer(),C=e.dom.animator;return{parent:"body",onShow:f,onRemove:p,hasBackdrop:!0,disableParentScroll:!0,skipCompile:!0,preserveScope:!0,skipHide:!0,themable:!0}}function i(e){return e instanceof t.element&&(e=e[0]),e}var r=8;return o.$inject=["$mdUtil","$mdTheming","$mdConstant","$document","$window","$q","$$rAF","$animateCss","$animate"],e("$mdMenu").setDefaults({methods:["target"],options:o})}t.module("material.components.menu").provider("$mdMenu",e),e.$inject=["$$interimElementProvider"]}(),function(){function e(e,n,i,r,a,d,s,c){this.$element=i,this.$attrs=r,this.$mdConstant=a,this.$mdUtil=s,this.$document=d,this.$scope=e,this.$rootScope=n,this.$timeout=c;var l=this;t.forEach(o,function(e){l[e]=t.bind(l,l[e])})}t.module("material.components.menuBar").controller("MenuBarController",e);var o=["handleKeyDown","handleMenuHover","scheduleOpenHoveredMenu","cancelScheduledOpen"];e.$inject=["$scope","$rootScope","$element","$attrs","$mdConstant","$document","$mdUtil","$timeout"],e.prototype.init=function(){var e=this.$element,t=this.$mdUtil,o=this.$scope,i=this,r=[];e.on("keydown",this.handleKeyDown),this.parentToolbar=t.getClosest(e,"MD-TOOLBAR"),r.push(this.$rootScope.$on("$mdMenuOpen",function(t,n){-1!=i.getMenus().indexOf(n[0])&&(e[0].classList.add("_md-open"),n[0].classList.add("_md-open"),i.currentlyOpenMenu=n.controller("mdMenu"),i.currentlyOpenMenu.registerContainerProxy(i.handleKeyDown),i.enableOpenOnHover())})),r.push(this.$rootScope.$on("$mdMenuClose",function(o,r,a){var d=i.getMenus();if(-1!=d.indexOf(r[0])&&(e[0].classList.remove("_md-open"),r[0].classList.remove("_md-open")),e[0].contains(r[0])){for(var s=r[0];s&&-1==d.indexOf(s);)s=t.getClosest(s,"MD-MENU",!0);s&&(a.skipFocus||s.querySelector("button:not([disabled])").focus(),i.currentlyOpenMenu=n,i.disableOpenOnHover(),i.setKeyboardMode(!0))}})),o.$on("$destroy",function(){for(;r.length;)r.shift()()}),this.setKeyboardMode(!0)},e.prototype.setKeyboardMode=function(e){e?this.$element[0].classList.add("_md-keyboard-mode"):this.$element[0].classList.remove("_md-keyboard-mode")},e.prototype.enableOpenOnHover=function(){if(!this.openOnHoverEnabled){this.openOnHoverEnabled=!0;var e;(e=this.parentToolbar)&&(e.dataset.mdRestoreStyle=e.getAttribute("style"),e.style.position="relative",e.style.zIndex=100),t.element(this.getMenus()).on("mouseenter",this.handleMenuHover)}},e.prototype.handleMenuHover=function(e){this.setKeyboardMode(!1),this.openOnHoverEnabled&&this.scheduleOpenHoveredMenu(e)},e.prototype.disableOpenOnHover=function(){if(this.openOnHoverEnabled){this.openOnHoverEnabled=!1;var e;(e=this.parentToolbar)&&(e.style.cssText=e.dataset.mdRestoreStyle||""),t.element(this.getMenus()).off("mouseenter",this.handleMenuHover)}},e.prototype.scheduleOpenHoveredMenu=function(e){var n=t.element(e.currentTarget),o=n.controller("mdMenu");this.setKeyboardMode(!1),this.scheduleOpenMenu(o)},e.prototype.scheduleOpenMenu=function(e){var t=this,o=this.$timeout;e!=t.currentlyOpenMenu&&(o.cancel(t.pendingMenuOpen),t.pendingMenuOpen=o(function(){t.pendingMenuOpen=n,t.currentlyOpenMenu&&t.currentlyOpenMenu.close(!0,{closeAll:!0}),e.open()},200,!1))},e.prototype.handleKeyDown=function(e){var n=this.$mdConstant.KEY_CODE,o=this.currentlyOpenMenu,i=o&&o.isOpen;this.setKeyboardMode(!0);var r,a,d;switch(e.keyCode){case n.DOWN_ARROW:o?o.focusMenuContainer():this.openFocusedMenu(),r=!0;break;case n.UP_ARROW:o&&o.close(),r=!0;break;case n.LEFT_ARROW:a=this.focusMenu(-1),i&&(d=t.element(a).controller("mdMenu"),this.scheduleOpenMenu(d)),r=!0;break;case n.RIGHT_ARROW:a=this.focusMenu(1),i&&(d=t.element(a).controller("mdMenu"),this.scheduleOpenMenu(d)),r=!0}r&&(e&&e.preventDefault&&e.preventDefault(),e&&e.stopImmediatePropagation&&e.stopImmediatePropagation())},e.prototype.focusMenu=function(e){var t=this.getMenus(),n=this.getFocusedMenuIndex();-1==n&&(n=this.getOpenMenuIndex());var o=!1;return-1==n?(n=0,o=!0):(0>e&&n>0||e>0&&n<t.length-e)&&(n+=e,o=!0),o?(t[n].querySelector("button").focus(),t[n]):void 0},e.prototype.openFocusedMenu=function(){var e=this.getFocusedMenu();e&&t.element(e).controller("mdMenu").open()},e.prototype.getMenus=function(){var e=this.$element;return this.$mdUtil.nodesToArray(e[0].children).filter(function(e){return"MD-MENU"==e.nodeName})},e.prototype.getFocusedMenu=function(){return this.getMenus()[this.getFocusedMenuIndex()]},e.prototype.getFocusedMenuIndex=function(){var e=this.$mdUtil,t=e.getClosest(this.$document[0].activeElement,"MD-MENU");if(!t)return-1;var n=this.getMenus().indexOf(t);return n},e.prototype.getOpenMenuIndex=function(){for(var e=this.getMenus(),t=0;t<e.length;++t)if(e[t].classList.contains("_md-open"))return t;return-1}}(),function(){function e(e,n){return{restrict:"E",require:"mdMenuBar",controller:"MenuBarController",compile:function(o,i){return i.ariaRole||o[0].setAttribute("role","menubar"),t.forEach(o[0].children,function(n){if("MD-MENU"==n.nodeName){n.hasAttribute("md-position-mode")||(n.setAttribute("md-position-mode","left bottom"),n.querySelector("button,a").setAttribute("role","menuitem"));var o=e.nodesToArray(n.querySelectorAll("md-menu-content"));t.forEach(o,function(e){e.classList.add("_md-menu-bar-menu"),e.classList.add("md-dense"),e.hasAttribute("width")||e.setAttribute("width",5)})}}),function(e,t,o,i){t.addClass("_md"),n(e,t),i.init()}}}}t.module("material.components.menuBar").directive("mdMenuBar",e),e.$inject=["$mdUtil","$mdTheming"]}(),function(){function e(){return{restrict:"E",compile:function(e,t){t.role||e[0].setAttribute("role","separator")}}}t.module("material.components.menuBar").directive("mdMenuDivider",e)}(),function(){function e(e,t,n){this.$element=t,this.$attrs=n,this.$scope=e}t.module("material.components.menuBar").controller("MenuItemController",e),e.$inject=["$scope","$element","$attrs"],e.prototype.init=function(e){var t=this.$element,n=this.$attrs;this.ngModel=e,"checkbox"!=n.type&&"radio"!=n.type||(this.mode=n.type,this.iconEl=t[0].children[0],this.buttonEl=t[0].children[1],e&&this.initClickListeners())},e.prototype.clearNgAria=function(){var e=this.$element[0],n=["role","tabindex","aria-invalid","aria-checked"];t.forEach(n,function(t){e.removeAttribute(t)})},e.prototype.initClickListeners=function(){function e(){if("radio"==d){var e=a.ngValue?r.$eval(a.ngValue):a.value;return i.$modelValue==e}return i.$modelValue}function n(e){e?c.off("click",l):c.on("click",l)}var o=this,i=this.ngModel,r=this.$scope,a=this.$attrs,d=(this.$element,this.mode);this.handleClick=t.bind(this,this.handleClick);var s=this.iconEl,c=t.element(this.buttonEl),l=this.handleClick;a.$observe("disabled",n),n(a.disabled),i.$render=function(){o.clearNgAria(),e()?(s.style.display="",c.attr("aria-checked","true")):(s.style.display="none",c.attr("aria-checked","false"))},r.$$postDigest(i.$render)},e.prototype.handleClick=function(e){var t,n=this.mode,o=this.ngModel,i=this.$attrs;"checkbox"==n?t=!o.$modelValue:"radio"==n&&(t=i.ngValue?this.$scope.$eval(i.ngValue):i.value),o.$setViewValue(t),o.$render()}}(),function(){function e(e){return{require:["mdMenuItem","?ngModel"],priority:210,compile:function(n,o){function i(e,o,i){i=i||n,i instanceof t.element&&(i=i[0]),i.hasAttribute(e)||i.setAttribute(e,o)}function r(e){if(n[0].hasAttribute(e)){var t=n[0].getAttribute(e);s[0].setAttribute(e,t),n[0].removeAttribute(e)}}function a(){return!!e.getClosest(n,"md-menu-bar",!0)}if(!a()||"checkbox"!=o.type&&"radio"!=o.type)i("role","menuitem",n[0].querySelector("md-button, button, a"));else{var d=n[0].textContent,s=t.element('<md-button type="button"></md-button>');s.html(d),s.attr("tabindex","0"),n.html(""),n.append(t.element('<md-icon md-svg-icon="check"></md-icon>')),n.append(s),n[0].classList.add("md-indent"),i("role","checkbox"==o.type?"menuitemcheckbox":"menuitemradio",s),t.forEach(["ng-disabled"],r)}return function(e,t,n,o){var i=o[0],r=o[1];i.init(r)}},controller:"MenuItemController"}}t.module("material.components.menuBar").directive("mdMenuItem",e),e.$inject=["$mdUtil"]}(),function(){function e(e,n,o,i,r,a){function d(a,d,c){function f(t,o,r,d,c){var l=++S,p=i.now(),f=o-t,g=m(a.mdDiameter),b=g-u(g),v=r||n.easeFn,E=d||n.duration;o===t?w.attr("d",s(o,g,b,c)):M=h(function $(n){var o=e.Math.max(0,e.Math.min((n||i.now())-p,E));w.attr("d",s(v(o,t,f,E),g,b,c)),l===S&&E>o&&(M=h($))})}function $(){f(k,x,n.easeFnIndeterminate,n.durationIndeterminate,N),N=(N+x)%100;var e=k;k=-x,x=-e}function C(){_||(_=r($,n.durationIndeterminate+50,0,!1),$(),d.addClass(E).removeAttr("aria-valuenow"))}function y(){_&&(r.cancel(_),_=null,d.removeClass(E))}var M,_,A=d[0],T=t.element(A.querySelector("svg")),w=t.element(A.querySelector("path")),k=n.startIndeterminate,x=n.endIndeterminate,N=0,S=0;o(d),d.toggleClass(v,c.hasOwnProperty("disabled")),a.mdMode===b&&C(),a.$on("$destroy",function(){y(),M&&p(M)}),a.$watchGroup(["value","mdMode",function(){var e=A.disabled;return e===!0||e===!1?e:t.isDefined(d.attr("disabled"))}],function(e,t){var n=e[1],o=e[2],i=t[2];if(o!==i&&d.toggleClass(v,!!o),o)y();else if(n!==g&&n!==b&&(n=b,c.$set("mdMode",n)),n===b)C();else{var r=l(e[0]);y(),d.attr("aria-valuenow",r),f(l(t[0]),r)}}),a.$watch("mdDiameter",function(e){var t=m(e),n=u(t),o=t/2+"px",i={width:t+"px",height:t+"px"};T[0].setAttribute("viewBox","0 0 "+t+" "+t),T.css(i).css("transform-origin",o+" "+o+" "+o),d.css(i),w.css("stroke-width",n+"px")})}function s(e,t,n,o){var i,r=3.5999,a=o||0,d=t/2,s=n/2,l=a*r,m=e*r,u=c(d,s,l),h=c(d,s,m+l),p=0>m?0:1;return i=0>m?m>=-180?0:1:180>=m?0:1,"M"+u+"A"+s+","+s+" 0 "+i+","+p+" "+h}function c(t,n,o){var i=(o-90)*f;return t+n*e.Math.cos(i)+","+(t+n*e.Math.sin(i))}function l(t){return e.Math.max(0,e.Math.min(t||0,100))}function m(e){var t=n.progressSize;if(e){var o=parseFloat(e);return e.lastIndexOf("%")===e.length-1&&(o=o/100*t),o}return t}function u(e){return n.strokeWidth/100*e}var h=e.requestAnimationFrame||t.noop,p=e.cancelAnimationFrame||t.noop,f=e.Math.PI/180,g="determinate",b="indeterminate",v="_md-progress-circular-disabled",E="_md-mode-indeterminate";return{restrict:"E",scope:{value:"@",mdDiameter:"@",mdMode:"@"},template:'<svg xmlns="http://www.w3.org/2000/svg"><path fill="none"/></svg>',compile:function(e,n){if(e.attr({"aria-valuemin":0,"aria-valuemax":100,role:"progressbar"}),t.isUndefined(n.mdMode)){var o=t.isDefined(n.value),i=o?g:b;n.$set("mdMode",i)}else n.$set("mdMode",n.mdMode.trim());return d}}}t.module("material.components.progressCircular").directive("mdProgressCircular",e),e.$inject=["$window","$mdProgressCircular","$mdTheming","$mdUtil","$interval","$log"]}(),function(){function e(){function e(e,t,n,o){return n*e/o+t}function n(e,t,n,o){var i=(e/=o)*e,r=i*e;return t+n*(6*r*i+-15*i*i+10*r)}var o={progressSize:50,strokeWidth:10,duration:100,easeFn:e,durationIndeterminate:500,startIndeterminate:3,endIndeterminate:80,easeFnIndeterminate:n,easingPresets:{linearEase:e,materialEase:n}};return{configure:function(e){return o=t.extend(o,e||{})},$get:function(){return o}}}t.module("material.components.progressCircular").provider("$mdProgressCircular",e)}(),function(){function e(){function e(e,o,i,r){if(r){var a=r.getTabElementIndex(o),d=n(o,"md-tab-body").remove(),s=n(o,"md-tab-label").remove(),c=r.insertTab({scope:e,parent:e.$parent,index:a,element:o,template:d.html(),label:s.html()},a);e.select=e.select||t.noop,e.deselect=e.deselect||t.noop,e.$watch("active",function(e){e&&r.select(c.getIndex(),!0)}),e.$watch("disabled",function(){r.refreshIndex()}),e.$watch(function(){return r.getTabElementIndex(o)},function(e){c.index=e,r.updateTabOrder()}),e.$on("$destroy",function(){r.removeTab(c)})}}function n(e,n){for(var o=e[0].children,i=0,r=o.length;r>i;i++){var a=o[i];if(a.tagName===n.toUpperCase())return t.element(a)}return t.element()}return{require:"^?mdTabs",terminal:!0,compile:function(o,i){var r=n(o,"md-tab-label"),a=n(o,"md-tab-body");if(0==r.length&&(r=t.element("<md-tab-label></md-tab-label>"),i.label?r.text(i.label):r.append(o.contents()),0==a.length)){var d=o.contents().detach();a=t.element("<md-tab-body></md-tab-body>"),a.append(d)}return o.append(r),a.html()&&o.append(a),e},scope:{active:"=?mdActive",disabled:"=?ngDisabled",select:"&?mdOnSelect",deselect:"&?mdOnDeselect"}}}t.module("material.components.tabs").directive("mdTab",e)}(),function(){function e(){return{require:"^?mdTabs",link:function(e,t,n,o){o&&o.attachRipple(e,t)}}}t.module("material.components.tabs").directive("mdTabItem",e)}(),function(){function e(){return{terminal:!0}}t.module("material.components.tabs").directive("mdTabLabel",e)}(),function(){function e(e){return{restrict:"A",compile:function(t,n){var o=e(n.mdTabScroll,null,!0);return function(e,t){t.on("mousewheel",function(t){e.$apply(function(){o(e,{$event:t})})})}}}}t.module("material.components.tabs").directive("mdTabScroll",e),e.$inject=["$parse"]}(),function(){function e(e,o,i,r,a,d,s,c,l,m){function u(){le.selectedIndex=le.selectedIndex||0,h(),f(),p(),m(o),d.nextTick(function(){ue=F(),re(),te(),ae(),le.tabs[le.selectedIndex]&&le.tabs[le.selectedIndex].scope.select(),fe=!0,Y()})}function h(){var e=c.$mdTabsTemplate,n=t.element(o[0].querySelector("md-tab-data"));n.html(e),l(n.contents())(le.parent),delete c.$mdTabsTemplate}function p(){t.element(i).on("resize",I),e.$on("$destroy",v)}function f(){e.$watch("$mdTabsCtrl.selectedIndex",T)}function g(e,t){var n=c.$normalize("md-"+e);t&&V(e,t),c.$observe(n,function(t){le[e]=t})}function b(e,t){function n(t){le[e]="false"!==t}var o=c.$normalize("md-"+e);t&&V(e,t),c.hasOwnProperty(o)&&n(c[o]),c.$observe(o,n)}function v(){pe=!0,t.element(i).off("resize",I)}function E(e){var n=F();t.element(n.wrapper).toggleClass("md-stretch-tabs",j()),ae()}function $(e){le.shouldCenterTabs=z()}function C(e,n){if(e!==n){var o=F();t.forEach(o.tabs,function(t){t.style.maxWidth=e+"px"}),d.nextTick(le.updateInkBarStyles)}}function y(e,t){e!==t&&(le.maxTabWidth=Q(),le.shouldCenterTabs=z(),d.nextTick(function(){le.maxTabWidth=Q(),te(le.selectedIndex)}))}function M(e){o[e?"removeClass":"addClass"]("md-no-tab-content")}function _(n){var o=F(),i=le.shouldCenterTabs?"":"-"+n+"px";t.element(o.paging).css(r.CSS.TRANSFORM,"translate3d("+i+", 0, 0)"),e.$broadcast("$mdTabsPaginationChanged")}function A(e,t){e!==t&&F().tabs[e]&&(te(),ee())}function T(t,n){t!==n&&(le.selectedIndex=W(t),le.lastSelectedIndex=n,le.updateInkBarStyles(),re(),te(t),e.$broadcast("$mdTabsChanged"),le.tabs[n]&&le.tabs[n].scope.deselect(),le.tabs[t]&&le.tabs[t].scope.select())}function w(e){var t=o[0].getElementsByTagName("md-tab");return Array.prototype.indexOf.call(t,e[0])}function k(){k.watcher||(k.watcher=e.$watch(function(){d.nextTick(function(){k.watcher&&o.prop("offsetParent")&&(k.watcher(),k.watcher=null,I())},!1)}))}function x(e){switch(e.keyCode){case r.KEY_CODE.LEFT_ARROW:e.preventDefault(),J(-1,!0);break;case r.KEY_CODE.RIGHT_ARROW:e.preventDefault(),J(1,!0);break;case r.KEY_CODE.SPACE:case r.KEY_CODE.ENTER:e.preventDefault(),me||N(le.focusIndex)}le.lastClick=!1}function N(e,t){me||(le.focusIndex=le.selectedIndex=e),le.lastClick=!0,t&&le.noSelectClick||d.nextTick(function(){le.tabs[e].element.triggerHandler("click")},!1)}function S(e){le.shouldPaginate&&(e.preventDefault(),le.offsetLeft=se(le.offsetLeft-e.wheelDelta))}function D(){var e,t,n=F(),o=n.canvas.clientWidth,i=o+le.offsetLeft;for(e=0;e<n.tabs.length&&(t=n.tabs[e],!(t.offsetLeft+t.offsetWidth>i));e++);le.offsetLeft=se(t.offsetLeft)}function H(){var e,t,n=F();for(e=0;e<n.tabs.length&&(t=n.tabs[e],!(t.offsetLeft+t.offsetWidth>=le.offsetLeft));e++);le.offsetLeft=se(t.offsetLeft+t.offsetWidth-n.canvas.clientWidth)}function I(){le.lastSelectedIndex=le.selectedIndex,le.offsetLeft=se(le.offsetLeft),d.nextTick(function(){le.updateInkBarStyles(),Y()})}function O(e){t.element(F().inkBar).toggleClass("ng-hide",e)}function R(e){o.toggleClass("md-dynamic-height",e)}function L(e){if(!pe){var t=le.selectedIndex,n=le.tabs.splice(e.getIndex(),1)[0];ie(),le.selectedIndex===t&&(n.scope.deselect(),le.tabs[le.selectedIndex]&&le.tabs[le.selectedIndex].scope.select()),d.nextTick(function(){Y(),le.offsetLeft=se(le.offsetLeft)})}}function P(e,n){var o=fe,i={getIndex:function(){return le.tabs.indexOf(r)},isActive:function(){return this.getIndex()===le.selectedIndex},isLeft:function(){return this.getIndex()<le.selectedIndex},isRight:function(){return this.getIndex()>le.selectedIndex},shouldRender:function(){return!le.noDisconnect||this.isActive()},hasFocus:function(){return!le.lastClick&&le.hasFocus&&this.getIndex()===le.focusIndex},id:d.nextUid()},r=t.extend(i,e);return t.isDefined(n)?le.tabs.splice(n,0,r):le.tabs.push(r),ne(),oe(),d.nextTick(function(){Y(),o&&le.autoselect&&d.nextTick(function(){d.nextTick(function(){N(le.tabs.indexOf(r))})})}),r}function F(){var e={},t=o[0];return e.wrapper=t.querySelector("md-tabs-wrapper"),e.canvas=e.wrapper.querySelector("md-tabs-canvas"),e.paging=e.canvas.querySelector("md-pagination-wrapper"),e.inkBar=e.paging.querySelector("md-ink-bar"),e.contents=t.querySelectorAll("md-tabs-content-wrapper > md-tab-content"),e.tabs=e.paging.querySelectorAll("md-tab-item"),e.dummies=e.canvas.querySelectorAll("md-dummy-tab"),e}function B(){return le.offsetLeft>0}function U(){var e=F(),t=e.tabs[e.tabs.length-1];return t&&t.offsetLeft+t.offsetWidth>e.canvas.clientWidth+le.offsetLeft}function j(){switch(le.stretchTabs){case"always":return!0;case"never":return!1;default:return!le.shouldPaginate&&i.matchMedia("(max-width: 600px)").matches}}function z(){return le.centerTabs&&!le.shouldPaginate}function q(){if(le.noPagination||!fe)return!1;var e=o.prop("clientWidth");return t.forEach(F().dummies,function(t){e-=t.offsetWidth}),0>e}function W(e){ +if(-1===e)return-1;var t,n,o=Math.max(le.tabs.length-e,e);for(t=0;o>=t;t++){if(n=le.tabs[e+t],n&&n.scope.disabled!==!0)return n.getIndex();if(n=le.tabs[e-t],n&&n.scope.disabled!==!0)return n.getIndex()}return e}function V(e,t,n){Object.defineProperty(le,e,{get:function(){return n},set:function(e){var o=n;n=e,t&&t(e,o)}})}function Y(){K(),le.maxTabWidth=Q(),le.shouldPaginate=q()}function K(){var e=F();j()?t.element(e.paging).css("width",""):t.element(e.paging).css("width",G()+"px")}function G(){return X(F().dummies)}function X(e){var n=0;return t.forEach(e,function(e){n+=Math.max(e.offsetWidth,e.getBoundingClientRect().width)}),Math.ceil(n)}function Q(){return o.prop("clientWidth")}function Z(){var e=le.tabs[le.selectedIndex],t=le.tabs[le.focusIndex];le.tabs=le.tabs.sort(function(e,t){return e.index-t.index}),le.selectedIndex=le.tabs.indexOf(e),le.focusIndex=le.tabs.indexOf(t)}function J(e,t){var n,o=t?"focusIndex":"selectedIndex",i=le[o];for(n=i+e;le.tabs[n]&&le.tabs[n].scope.disabled;n+=e);le.tabs[n]&&(le[o]=n)}function ee(){F().dummies[le.focusIndex].focus()}function te(e){var t=F();if(null==e&&(e=le.focusIndex),t.tabs[e]&&!le.shouldCenterTabs){var n=t.tabs[e],o=n.offsetLeft,i=n.offsetWidth+o;le.offsetLeft=Math.max(le.offsetLeft,se(i-t.canvas.clientWidth+64)),le.offsetLeft=Math.min(le.offsetLeft,se(o))}}function ne(){he.forEach(function(e){d.nextTick(e)}),he=[]}function oe(){var e=!1;t.forEach(le.tabs,function(t){t.template&&(e=!0)}),le.hasContent=e}function ie(){le.selectedIndex=W(le.selectedIndex),le.focusIndex=W(le.focusIndex)}function re(){if(!le.dynamicHeight)return o.css("height","");if(!le.tabs.length)return he.push(re);var e=F(),t=e.contents[le.selectedIndex],i=t?t.offsetHeight:0,r=e.wrapper.offsetHeight,a=i+r,c=o.prop("clientHeight");if(c!==a){"bottom"===o.attr("md-align-tabs")&&(c-=r,a-=r,o.attr("md-border-bottom")!==n&&++c),me=!0;var l={height:c+"px"},m={height:a+"px"};o.css(l),s(o,{from:l,to:m,easing:"cubic-bezier(0.35, 0, 0.25, 1)",duration:.5}).start().done(function(){o.css({transition:"none",height:""}),d.nextTick(function(){o.css("transition","")}),me=!1})}}function ae(){var e=F();if(!e.tabs[le.selectedIndex])return void t.element(e.inkBar).css({left:"auto",right:"auto"});if(!le.tabs.length)return he.push(le.updateInkBarStyles);if(!o.prop("offsetParent"))return k();var n=le.selectedIndex,i=e.paging.offsetWidth,r=e.tabs[n],a=r.offsetLeft,s=i-a-r.offsetWidth;if(le.shouldCenterTabs){var c=X(e.tabs);i>c&&d.nextTick(ae,!1)}de(),t.element(e.inkBar).css({left:a+"px",right:s+"px"})}function de(){var e=F(),n=le.selectedIndex,o=le.lastSelectedIndex,i=t.element(e.inkBar);t.isNumber(o)&&i.toggleClass("md-left",o>n).toggleClass("md-right",n>o)}function se(e){var t=F();if(!t.tabs.length||!le.shouldPaginate)return 0;var n=t.tabs[t.tabs.length-1],o=n.offsetLeft+n.offsetWidth;return e=Math.max(0,e),e=Math.min(o-t.canvas.clientWidth,e)}function ce(e,n){var o=F(),i={colorElement:t.element(o.inkBar)};a.attach(e,n,i)}var le=this,me=!1,ue=F(),he=[],pe=!1,fe=!1;g("stretchTabs",E),V("focusIndex",A,le.selectedIndex||0),V("offsetLeft",_,0),V("hasContent",M,!1),V("maxTabWidth",C,Q()),V("shouldPaginate",y,!1),b("noInkBar",O),b("dynamicHeight",R),b("noPagination"),b("swipeContent"),b("noDisconnect"),b("autoselect"),b("noSelectClick"),b("centerTabs",$,!1),b("enableDisconnect"),le.scope=e,le.parent=e.$parent,le.tabs=[],le.lastSelectedIndex=null,le.hasFocus=!1,le.lastClick=!0,le.shouldCenterTabs=z(),le.updatePagination=d.debounce(Y,100),le.redirectFocus=ee,le.attachRipple=ce,le.insertTab=P,le.removeTab=L,le.select=N,le.scroll=S,le.nextPage=D,le.previousPage=H,le.keydown=x,le.canPageForward=U,le.canPageBack=B,le.refreshIndex=ie,le.incrementIndex=J,le.getTabElementIndex=w,le.updateInkBarStyles=d.debounce(ae,100),le.updateTabOrder=d.debounce(Z,100),u()}t.module("material.components.tabs").controller("MdTabsController",e),e.$inject=["$scope","$element","$window","$mdConstant","$mdTabInkRipple","$mdUtil","$animateCss","$attrs","$compile","$mdTheming"]}(),function(){function e(e){return{scope:{selectedIndex:"=?mdSelected"},template:function(t,n){return n.$mdTabsTemplate=t.html(),'<md-tabs-wrapper> <md-tab-data></md-tab-data> <md-prev-button tabindex="-1" role="button" aria-label="Previous Page" aria-disabled="{{!$mdTabsCtrl.canPageBack()}}" ng-class="{ \'md-disabled\': !$mdTabsCtrl.canPageBack() }" ng-if="$mdTabsCtrl.shouldPaginate" ng-click="$mdTabsCtrl.previousPage()"> <md-icon md-svg-src="'+e.mdTabsArrow+'"></md-icon> </md-prev-button> <md-next-button tabindex="-1" role="button" aria-label="Next Page" aria-disabled="{{!$mdTabsCtrl.canPageForward()}}" ng-class="{ \'md-disabled\': !$mdTabsCtrl.canPageForward() }" ng-if="$mdTabsCtrl.shouldPaginate" ng-click="$mdTabsCtrl.nextPage()"> <md-icon md-svg-src="'+e.mdTabsArrow+'"></md-icon> </md-next-button> <md-tabs-canvas tabindex="{{ $mdTabsCtrl.hasFocus ? -1 : 0 }}" aria-activedescendant="tab-item-{{$mdTabsCtrl.tabs[$mdTabsCtrl.focusIndex].id}}" ng-focus="$mdTabsCtrl.redirectFocus()" ng-class="{ \'md-paginated\': $mdTabsCtrl.shouldPaginate, \'md-center-tabs\': $mdTabsCtrl.shouldCenterTabs }" ng-keydown="$mdTabsCtrl.keydown($event)" role="tablist"> <md-pagination-wrapper ng-class="{ \'md-center-tabs\': $mdTabsCtrl.shouldCenterTabs }" md-tab-scroll="$mdTabsCtrl.scroll($event)"> <md-tab-item tabindex="-1" class="md-tab" ng-repeat="tab in $mdTabsCtrl.tabs" role="tab" aria-controls="tab-content-{{::tab.id}}" aria-selected="{{tab.isActive()}}" aria-disabled="{{tab.scope.disabled || \'false\'}}" ng-click="$mdTabsCtrl.select(tab.getIndex())" ng-class="{ \'md-active\': tab.isActive(), \'md-focused\': tab.hasFocus(), \'md-disabled\': tab.scope.disabled }" ng-disabled="tab.scope.disabled" md-swipe-left="$mdTabsCtrl.nextPage()" md-swipe-right="$mdTabsCtrl.previousPage()" md-tabs-template="::tab.label" md-scope="::tab.parent"></md-tab-item> <md-ink-bar></md-ink-bar> </md-pagination-wrapper> <md-tabs-dummy-wrapper class="_md-visually-hidden md-dummy-wrapper"> <md-dummy-tab class="md-tab" tabindex="-1" id="tab-item-{{::tab.id}}" role="tab" aria-controls="tab-content-{{::tab.id}}" aria-selected="{{tab.isActive()}}" aria-disabled="{{tab.scope.disabled || \'false\'}}" ng-focus="$mdTabsCtrl.hasFocus = true" ng-blur="$mdTabsCtrl.hasFocus = false" ng-repeat="tab in $mdTabsCtrl.tabs" md-tabs-template="::tab.label" md-scope="::tab.parent"></md-dummy-tab> </md-tabs-dummy-wrapper> </md-tabs-canvas> </md-tabs-wrapper> <md-tabs-content-wrapper ng-show="$mdTabsCtrl.hasContent && $mdTabsCtrl.selectedIndex >= 0" class="_md"> <md-tab-content id="tab-content-{{::tab.id}}" class="_md" role="tabpanel" aria-labelledby="tab-item-{{::tab.id}}" md-swipe-left="$mdTabsCtrl.swipeContent && $mdTabsCtrl.incrementIndex(1)" md-swipe-right="$mdTabsCtrl.swipeContent && $mdTabsCtrl.incrementIndex(-1)" ng-if="$mdTabsCtrl.hasContent" ng-repeat="(index, tab) in $mdTabsCtrl.tabs" ng-class="{ \'md-no-transition\': $mdTabsCtrl.lastSelectedIndex == null, \'md-active\': tab.isActive(), \'md-left\': tab.isLeft(), \'md-right\': tab.isRight(), \'md-no-scroll\': $mdTabsCtrl.dynamicHeight }"> <div md-tabs-template="::tab.template" md-connected-if="tab.isActive()" md-scope="::tab.parent" ng-if="$mdTabsCtrl.enableDisconnect || tab.shouldRender()"></div> </md-tab-content> </md-tabs-content-wrapper>'},controller:"MdTabsController",controllerAs:"$mdTabsCtrl",bindToController:!0}}t.module("material.components.tabs").directive("mdTabs",e),e.$inject=["$$mdSvgRegistry"]}(),function(){function e(e){return{require:"^?mdTabs",link:function(e,t,n,o){if(o){var i=new MutationObserver(function(e){o.updatePagination(),o.updateInkBarStyles()}),r={childList:!0,subtree:!0};i.observe(t[0],r),e.$on("$destroy",function(){i&&i.disconnect()})}}}}t.module("material.components.tabs").directive("mdTabsDummyWrapper",e),e.$inject=["$mdUtil"]}(),function(){function e(e,t){function n(n,o,i,r){function a(){n.$watch("connected",function(e){e===!1?d():s()}),n.$on("$destroy",s)}function d(){r.enableDisconnect&&t.disconnectScope(c)}function s(){r.enableDisconnect&&t.reconnectScope(c)}if(r){var c=r.enableDisconnect?n.compileScope.$new():n.compileScope;return o.html(n.template),e(o.contents())(c),t.nextTick(a)}}return{restrict:"A",link:n,scope:{template:"=mdTabsTemplate",connected:"=?mdConnectedIf",compileScope:"=mdScope"},require:"^?mdTabs"}}t.module("material.components.tabs").directive("mdTabsTemplate",e),e.$inject=["$compile","$mdUtil"]}(),function(){t.module("material.core").constant("$MD_THEME_CSS","/* Only used with Theme processes */html.md-THEME_NAME-theme, body.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-color}}'; }md-autocomplete.md-THEME_NAME-theme { background: '{{background-A100}}'; } md-autocomplete.md-THEME_NAME-theme[disabled]:not([md-floating-label]) { background: '{{background-100}}'; } md-autocomplete.md-THEME_NAME-theme button md-icon path { fill: '{{background-600}}'; } md-autocomplete.md-THEME_NAME-theme button:after { background: '{{background-600-0.3}}'; }.md-autocomplete-suggestions-container.md-THEME_NAME-theme { background: '{{background-A100}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li { color: '{{background-900}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li .highlight { color: '{{background-600}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li:hover, .md-autocomplete-suggestions-container.md-THEME_NAME-theme li.selected { background: '{{background-200}}'; }md-backdrop { background-color: '{{background-900-0.0}}'; } md-backdrop.md-opaque.md-THEME_NAME-theme { background-color: '{{background-900-1.0}}'; }.md-button.md-THEME_NAME-theme:not([disabled]):hover { background-color: '{{background-500-0.2}}'; }.md-button.md-THEME_NAME-theme:not([disabled]).md-focused { background-color: '{{background-500-0.2}}'; }.md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover { background-color: transparent; }.md-button.md-THEME_NAME-theme.md-fab { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; }.md-button.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised, .md-button.md-THEME_NAME-theme.md-primary.md-fab { color: '{{primary-contrast}}'; background-color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]) md-icon { color: '{{primary-contrast}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]).md-focused { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon { color: '{{primary-color}}'; }.md-button.md-THEME_NAME-theme.md-fab { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; }.md-button.md-THEME_NAME-theme.md-raised { color: '{{background-900}}'; background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]) md-icon { color: '{{background-900}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover { background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused { background-color: '{{background-200}}'; }.md-button.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised, .md-button.md-THEME_NAME-theme.md-warn.md-fab { color: '{{warn-contrast}}'; background-color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]) md-icon { color: '{{warn-contrast}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover { background-color: '{{warn-600}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]).md-focused { background-color: '{{warn-600}}'; } .md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon { color: '{{warn-color}}'; }.md-button.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised, .md-button.md-THEME_NAME-theme.md-accent.md-fab { color: '{{accent-contrast}}'; background-color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]) md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon { color: '{{accent-color}}'; }.md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled], .md-button.md-THEME_NAME-theme.md-accent[disabled], .md-button.md-THEME_NAME-theme.md-warn[disabled] { color: '{{foreground-3}}'; cursor: default; } .md-button.md-THEME_NAME-theme[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-raised[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-fab[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-accent[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-warn[disabled] md-icon { color: '{{foreground-3}}'; }.md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] { background-color: '{{foreground-4}}'; }.md-button.md-THEME_NAME-theme[disabled] { background-color: transparent; }._md a.md-THEME_NAME-theme:not(.md-button).md-primary { color: '{{primary-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-primary:hover { color: '{{primary-700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-accent { color: '{{accent-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-accent:hover { color: '{{accent-700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-accent { color: '{{accent-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-accent:hover { color: '{{accent-A700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-warn { color: '{{warn-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-warn:hover { color: '{{warn-700}}'; }md-bottom-sheet.md-THEME_NAME-theme { background-color: '{{background-50}}'; border-top-color: '{{background-300}}'; } md-bottom-sheet.md-THEME_NAME-theme.md-list md-list-item { color: '{{foreground-1}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { background-color: '{{background-50}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { color: '{{foreground-1}}'; }md-checkbox.md-THEME_NAME-theme .md-ripple { color: '{{accent-A700}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked.md-focused ._md-container:before { background-color: '{{accent-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme .md-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme ._md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked ._md-icon { background-color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme.md-checked ._md-icon:after { border-color: '{{accent-contrast-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-ripple { color: '{{primary-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary ._md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked ._md-icon { background-color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked.md-focused ._md-container:before { background-color: '{{primary-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked ._md-icon:after { border-color: '{{primary-contrast-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-indeterminate[disabled] ._md-container { color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-ripple { color: '{{warn-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn ._md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked ._md-icon { background-color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked.md-focused:not([disabled]) ._md-container:before { background-color: '{{warn-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked ._md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme[disabled] ._md-icon { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked ._md-icon { background-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked ._md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme[disabled] ._md-icon:after { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled] ._md-label { color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme .md-chips { box-shadow: 0 1px '{{foreground-4}}'; } md-chips.md-THEME_NAME-theme .md-chips.md-focused { box-shadow: 0 2px '{{primary-color}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input { color: '{{foreground-1}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input::-webkit-input-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input:-moz-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input::-moz-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input:-ms-input-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips ._md-chip-input-container input::-webkit-input-placeholder { color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme md-chip { background: '{{background-300}}'; color: '{{background-800}}'; } md-chips.md-THEME_NAME-theme md-chip md-icon { color: '{{background-700}}'; } md-chips.md-THEME_NAME-theme md-chip.md-focused { background: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-chips.md-THEME_NAME-theme md-chip.md-focused md-icon { color: '{{primary-contrast}}'; } md-chips.md-THEME_NAME-theme md-chip._md-chip-editing { background: transparent; color: '{{background-800}}'; }md-chips.md-THEME_NAME-theme md-chip-remove .md-button md-icon path { fill: '{{background-500}}'; }.md-contact-suggestion span.md-contact-email { color: '{{background-400}}'; }md-card.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-hue-1}}'; border-radius: 2px; } md-card.md-THEME_NAME-theme .md-card-image { border-radius: 2px 2px 0 0; } md-card.md-THEME_NAME-theme md-card-header md-card-avatar md-icon { color: '{{background-color}}'; background-color: '{{foreground-3}}'; } md-card.md-THEME_NAME-theme md-card-header md-card-header-text .md-subhead { color: '{{foreground-2}}'; } md-card.md-THEME_NAME-theme md-card-title md-card-title-text:not(:only-child) .md-subhead { color: '{{foreground-2}}'; }md-content.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-default}}'; }/** Theme styles for mdCalendar. */.md-calendar.md-THEME_NAME-theme { background: '{{background-A100}}'; color: '{{background-A200-0.87}}'; } .md-calendar.md-THEME_NAME-theme tr:last-child td { border-bottom-color: '{{background-200}}'; }.md-THEME_NAME-theme .md-calendar-day-header { background: '{{background-300}}'; color: '{{background-A200-0.87}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-date-today .md-calendar-date-selection-indicator { border: 1px solid '{{primary-500}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-date-today.md-calendar-date-disabled { color: '{{primary-500-0.6}}'; }.md-calendar-date.md-focus .md-THEME_NAME-theme .md-calendar-date-selection-indicator, .md-THEME_NAME-theme .md-calendar-date-selection-indicator:hover { background: '{{background-300}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-selected-date .md-calendar-date-selection-indicator,.md-THEME_NAME-theme .md-calendar-date.md-focus.md-calendar-selected-date .md-calendar-date-selection-indicator { background: '{{primary-500}}'; color: '{{primary-500-contrast}}'; border-color: transparent; }.md-THEME_NAME-theme .md-calendar-date-disabled,.md-THEME_NAME-theme .md-calendar-month-label-disabled { color: '{{background-A200-0.435}}'; }/** Theme styles for mdDatepicker. */.md-THEME_NAME-theme .md-datepicker-input { color: '{{foreground-1}}'; } .md-THEME_NAME-theme .md-datepicker-input::-webkit-input-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input:-moz-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input::-moz-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input:-ms-input-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input::-webkit-input-placeholder { color: '{{foreground-3}}'; }.md-THEME_NAME-theme .md-datepicker-input-container { border-bottom-color: '{{foreground-4}}'; } .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-focused { border-bottom-color: '{{primary-color}}'; } .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-invalid { border-bottom-color: '{{warn-A700}}'; }.md-THEME_NAME-theme .md-datepicker-calendar-pane { border-color: '{{background-hue-1}}'; }.md-THEME_NAME-theme .md-datepicker-triangle-button .md-datepicker-expand-triangle { border-top-color: '{{foreground-3}}'; }.md-THEME_NAME-theme .md-datepicker-triangle-button:hover .md-datepicker-expand-triangle { border-top-color: '{{foreground-2}}'; }.md-THEME_NAME-theme .md-datepicker-open .md-datepicker-calendar-icon { fill: '{{primary-500}}'; }.md-THEME_NAME-theme .md-datepicker-open .md-datepicker-input-container,.md-THEME_NAME-theme .md-datepicker-input-mask-opaque { background: '{{background-hue-1}}'; }.md-THEME_NAME-theme .md-datepicker-calendar { background: '{{background-A100}}'; }md-dialog.md-THEME_NAME-theme { border-radius: 4px; background-color: '{{background-hue-1}}'; } md-dialog.md-THEME_NAME-theme.md-content-overflow .md-actions, md-dialog.md-THEME_NAME-theme.md-content-overflow md-dialog-actions { border-top-color: '{{foreground-4}}'; }md-divider.md-THEME_NAME-theme { border-top-color: '{{foreground-4}}'; }.layout-row > md-divider.md-THEME_NAME-theme,.layout-xs-row > md-divider.md-THEME_NAME-theme, .layout-gt-xs-row > md-divider.md-THEME_NAME-theme,.layout-sm-row > md-divider.md-THEME_NAME-theme, .layout-gt-sm-row > md-divider.md-THEME_NAME-theme,.layout-md-row > md-divider.md-THEME_NAME-theme, .layout-gt-md-row > md-divider.md-THEME_NAME-theme,.layout-lg-row > md-divider.md-THEME_NAME-theme, .layout-gt-lg-row > md-divider.md-THEME_NAME-theme,.layout-xl-row > md-divider.md-THEME_NAME-theme { border-right-color: '{{foreground-4}}'; }md-icon.md-THEME_NAME-theme { color: '{{foreground-2}}'; } md-icon.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } md-icon.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } md-icon.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-input-container.md-THEME_NAME-theme .md-input { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder { color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme > md-icon { color: '{{foreground-1}}'; }md-input-container.md-THEME_NAME-theme label,md-input-container.md-THEME_NAME-theme ._md-placeholder { color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme label.md-required:after { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-focused):not(.md-input-invalid) label.md-required:after { color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme .md-input-messages-animation, md-input-container.md-THEME_NAME-theme .md-input-message-animation { color: '{{warn-A700}}'; } md-input-container.md-THEME_NAME-theme .md-input-messages-animation .md-char-counter, md-input-container.md-THEME_NAME-theme .md-input-message-animation .md-char-counter { color: '{{foreground-1}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-has-value label { color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused .md-input, md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-resized .md-input { border-color: '{{primary-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused label { color: '{{primary-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused md-icon { color: '{{primary-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent .md-input { border-color: '{{accent-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent label { color: '{{accent-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn .md-input { border-color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn label { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input { border-color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid label { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input-message-animation, md-input-container.md-THEME_NAME-theme.md-input-invalid .md-char-counter { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme .md-input[disabled],[disabled] md-input-container.md-THEME_NAME-theme .md-input { border-bottom-color: transparent; color: '{{foreground-3}}'; background-image: linear-gradient(to right, \"{{foreground-3}}\" 0%, \"{{foreground-3}}\" 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, \"{{foreground-3}}\" 100%); }md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text h3, md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text h4,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text h3,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text h4 { color: '{{foreground-1}}'; }md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text p,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text p { color: '{{foreground-2}}'; }md-list.md-THEME_NAME-theme ._md-proxy-focus.md-focused div._md-no-style { background-color: '{{background-100}}'; }md-list.md-THEME_NAME-theme md-list-item .md-avatar-icon { background-color: '{{foreground-3}}'; color: '{{background-color}}'; }md-list.md-THEME_NAME-theme md-list-item > md-icon { color: '{{foreground-2}}'; } md-list.md-THEME_NAME-theme md-list-item > md-icon.md-highlight { color: '{{primary-color}}'; } md-list.md-THEME_NAME-theme md-list-item > md-icon.md-highlight.md-accent { color: '{{accent-color}}'; }md-menu-content.md-THEME_NAME-theme { background-color: '{{background-A100}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item { color: '{{background-A200-0.87}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item md-icon { color: '{{background-A200-0.54}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item .md-button[disabled] { color: '{{background-A200-0.25}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item .md-button[disabled] md-icon { color: '{{background-A200-0.25}}'; } md-menu-content.md-THEME_NAME-theme md-menu-divider { background-color: '{{background-A200-0.11}}'; }md-menu-bar.md-THEME_NAME-theme > button.md-button { color: '{{foreground-2}}'; border-radius: 2px; }md-menu-bar.md-THEME_NAME-theme md-menu._md-open > button, md-menu-bar.md-THEME_NAME-theme md-menu > button:focus { outline: none; background: '{{background-200}}'; }md-menu-bar.md-THEME_NAME-theme._md-open:not(._md-keyboard-mode) md-menu:hover > button { background-color: '{{ background-500-0.2}}'; }md-menu-bar.md-THEME_NAME-theme:not(._md-keyboard-mode):not(._md-open) md-menu button:hover,md-menu-bar.md-THEME_NAME-theme:not(._md-keyboard-mode):not(._md-open) md-menu button:focus { background: transparent; }md-menu-content.md-THEME_NAME-theme .md-menu > .md-button:after { color: '{{background-A200-0.54}}'; }md-menu-content.md-THEME_NAME-theme .md-menu._md-open > .md-button { background-color: '{{ background-500-0.2}}'; }md-toolbar.md-THEME_NAME-theme.md-menu-toolbar { background-color: '{{background-A100}}'; color: '{{background-A200}}'; } md-toolbar.md-THEME_NAME-theme.md-menu-toolbar md-toolbar-filler { background-color: '{{primary-color}}'; color: '{{background-A100-0.87}}'; } md-toolbar.md-THEME_NAME-theme.md-menu-toolbar md-toolbar-filler md-icon { color: '{{background-A100-0.87}}'; }md-nav-bar.md-THEME_NAME-theme .md-nav-bar { background-color: transparent; border-color: '{{foreground-4}}'; }md-nav-bar.md-THEME_NAME-theme .md-button._md-nav-button.md-unselected { color: '{{foreground-2}}'; }md-nav-bar.md-THEME_NAME-theme md-nav-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }.md-panel { background-color: '{{background-900-0.0}}'; } .md-panel._md-panel-backdrop.md-THEME_NAME-theme { background-color: '{{background-900-1.0}}'; }md-progress-linear.md-THEME_NAME-theme ._md-container { background-color: '{{primary-100}}'; }md-progress-linear.md-THEME_NAME-theme ._md-bar { background-color: '{{primary-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn ._md-container { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn ._md-bar { background-color: '{{warn-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent ._md-container { background-color: '{{accent-A100}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent ._md-bar { background-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn ._md-bar1 { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn ._md-dashed:before { background: radial-gradient(\"{{warn-100}}\" 0%, \"{{warn-100}}\" 16%, transparent 42%); }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent ._md-bar1 { background-color: '{{accent-A100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent ._md-dashed:before { background: radial-gradient(\"{{accent-A100}}\" 0%, \"{{accent-A100}}\" 16%, transparent 42%); }md-progress-circular.md-THEME_NAME-theme path { stroke: '{{primary-color}}'; }md-progress-circular.md-THEME_NAME-theme.md-warn path { stroke: '{{warn-color}}'; }md-progress-circular.md-THEME_NAME-theme.md-accent path { stroke: '{{accent-color}}'; }md-radio-button.md-THEME_NAME-theme ._md-off { border-color: '{{foreground-2}}'; }md-radio-button.md-THEME_NAME-theme ._md-on { background-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked ._md-off { border-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme ._md-container .md-ripple { color: '{{accent-A700}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary ._md-on, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary ._md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary ._md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary ._md-on { background-color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked ._md-off { border-color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary ._md-container .md-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary ._md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary ._md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary ._md-container .md-ripple { color: '{{primary-600}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn ._md-on, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn ._md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn ._md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn ._md-on { background-color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked ._md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked ._md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked ._md-off { border-color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn ._md-container .md-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn ._md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn ._md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn ._md-container .md-ripple { color: '{{warn-600}}'; }md-radio-group.md-THEME_NAME-theme[disabled],md-radio-button.md-THEME_NAME-theme[disabled] { color: '{{foreground-3}}'; } md-radio-group.md-THEME_NAME-theme[disabled] ._md-container ._md-off, md-radio-button.md-THEME_NAME-theme[disabled] ._md-container ._md-off { border-color: '{{foreground-3}}'; } md-radio-group.md-THEME_NAME-theme[disabled] ._md-container ._md-on, md-radio-button.md-THEME_NAME-theme[disabled] ._md-container ._md-on { border-color: '{{foreground-3}}'; }md-radio-group.md-THEME_NAME-theme .md-checked .md-ink-ripple { color: '{{accent-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-primary .md-checked:not([disabled]) .md-ink-ripple, md-radio-group.md-THEME_NAME-theme .md-checked:not([disabled]).md-primary .md-ink-ripple { color: '{{primary-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme .md-checked.md-primary .md-ink-ripple { color: '{{warn-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked ._md-container:before { background-color: '{{accent-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty).md-primary .md-checked ._md-container:before,md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked.md-primary ._md-container:before { background-color: '{{primary-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty).md-warn .md-checked ._md-container:before,md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked.md-warn ._md-container:before { background-color: '{{warn-color-0.26}}'; }md-select.md-THEME_NAME-theme[disabled] ._md-select-value { border-bottom-color: transparent; background-image: linear-gradient(to right, \"{{foreground-3}}\" 0%, \"{{foreground-3}}\" 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, \"{{foreground-3}}\" 100%); }md-select.md-THEME_NAME-theme ._md-select-value { border-bottom-color: '{{foreground-4}}'; } md-select.md-THEME_NAME-theme ._md-select-value._md-select-placeholder { color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme.ng-invalid.ng-dirty ._md-select-value { color: '{{warn-A700}}' !important; border-bottom-color: '{{warn-A700}}' !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus ._md-select-value { border-bottom-color: '{{primary-color}}'; color: '{{ foreground-1 }}'; } md-select.md-THEME_NAME-theme:not([disabled]):focus ._md-select-value._md-select-placeholder { color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent ._md-select-value { border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn ._md-select-value { border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] ._md-select-value { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme[disabled] ._md-select-value._md-select-placeholder { color: '{{foreground-3}}'; }md-select-menu.md-THEME_NAME-theme md-content { background: '{{background-A100}}'; } md-select-menu.md-THEME_NAME-theme md-content md-optgroup { color: '{{background-600-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option { color: '{{background-900-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[disabled] ._md-text { color: '{{background-400-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option:not([disabled]):focus, md-select-menu.md-THEME_NAME-theme md-content md-option:not([disabled]):hover { background: '{{background-200}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected] { color: '{{primary-500}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected]:focus { color: '{{primary-600}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected].md-accent { color: '{{accent-color}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected].md-accent:focus { color: '{{accent-A700}}'; }._md-checkbox-enabled.md-THEME_NAME-theme .md-ripple { color: '{{primary-600}}'; }._md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-ripple { color: '{{background-600}}'; }._md-checkbox-enabled.md-THEME_NAME-theme .md-ink-ripple { color: '{{foreground-2}}'; }._md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-ink-ripple { color: '{{primary-color-0.87}}'; }._md-checkbox-enabled.md-THEME_NAME-theme ._md-icon { border-color: '{{foreground-2}}'; }._md-checkbox-enabled.md-THEME_NAME-theme[selected] ._md-icon { background-color: '{{primary-color-0.87}}'; }._md-checkbox-enabled.md-THEME_NAME-theme[selected].md-focused ._md-container:before { background-color: '{{primary-color-0.26}}'; }._md-checkbox-enabled.md-THEME_NAME-theme[selected] ._md-icon:after { border-color: '{{primary-contrast-0.87}}'; }._md-checkbox-enabled.md-THEME_NAME-theme .md-indeterminate[disabled] ._md-container { color: '{{foreground-3}}'; }._md-checkbox-enabled.md-THEME_NAME-theme md-option ._md-text { color: '{{background-900-0.87}}'; }md-sidenav.md-THEME_NAME-theme, md-sidenav.md-THEME_NAME-theme md-content { background-color: '{{background-hue-1}}'; }md-slider.md-THEME_NAME-theme ._md-track { background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme ._md-track-ticks { color: '{{background-contrast}}'; }md-slider.md-THEME_NAME-theme ._md-focus-ring { background-color: '{{accent-A200-0.2}}'; }md-slider.md-THEME_NAME-theme ._md-disabled-thumb { border-color: '{{background-color}}'; background-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme._md-min ._md-thumb:after { background-color: '{{background-color}}'; border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme._md-min ._md-focus-ring { background-color: '{{foreground-3-0.38}}'; }md-slider.md-THEME_NAME-theme._md-min[md-discrete] ._md-thumb:after { background-color: '{{background-contrast}}'; border-color: transparent; }md-slider.md-THEME_NAME-theme._md-min[md-discrete] ._md-sign { background-color: '{{background-400}}'; } md-slider.md-THEME_NAME-theme._md-min[md-discrete] ._md-sign:after { border-top-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme._md-min[md-discrete][md-vertical] ._md-sign:after { border-top-color: transparent; border-left-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme ._md-track._md-track-fill { background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme ._md-thumb:after { border-color: '{{accent-color}}'; background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme ._md-sign { background-color: '{{accent-color}}'; } md-slider.md-THEME_NAME-theme ._md-sign:after { border-top-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme[md-vertical] ._md-sign:after { border-top-color: transparent; border-left-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme ._md-thumb-text { color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn ._md-focus-ring { background-color: '{{warn-200-0.38}}'; }md-slider.md-THEME_NAME-theme.md-warn ._md-track._md-track-fill { background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn ._md-thumb:after { border-color: '{{warn-color}}'; background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn ._md-sign { background-color: '{{warn-color}}'; } md-slider.md-THEME_NAME-theme.md-warn ._md-sign:after { border-top-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn[md-vertical] ._md-sign:after { border-top-color: transparent; border-left-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn ._md-thumb-text { color: '{{warn-contrast}}'; }md-slider.md-THEME_NAME-theme.md-primary ._md-focus-ring { background-color: '{{primary-200-0.38}}'; }md-slider.md-THEME_NAME-theme.md-primary ._md-track._md-track-fill { background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary ._md-thumb:after { border-color: '{{primary-color}}'; background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary ._md-sign { background-color: '{{primary-color}}'; } md-slider.md-THEME_NAME-theme.md-primary ._md-sign:after { border-top-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary[md-vertical] ._md-sign:after { border-top-color: transparent; border-left-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary ._md-thumb-text { color: '{{primary-contrast}}'; }md-slider.md-THEME_NAME-theme[disabled] ._md-thumb:after { border-color: transparent; }md-slider.md-THEME_NAME-theme[disabled]:not(._md-min) ._md-thumb:after, md-slider.md-THEME_NAME-theme[disabled][md-discrete] ._md-thumb:after { background-color: '{{foreground-3}}'; border-color: transparent; }md-slider.md-THEME_NAME-theme[disabled][readonly] ._md-sign { background-color: '{{background-400}}'; } md-slider.md-THEME_NAME-theme[disabled][readonly] ._md-sign:after { border-top-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme[disabled][readonly][md-vertical] ._md-sign:after { border-top-color: transparent; border-left-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme[disabled][readonly] ._md-disabled-thumb { border-color: transparent; background-color: transparent; }md-slider-container[disabled] > *:first-child:not(md-slider),md-slider-container[disabled] > *:last-child:not(md-slider) { color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme { color: '{{ foreground-2-0.23 }}'; background-color: '{{background-default}}'; } .md-subheader.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-subheader.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-subheader.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme .md-ink-ripple { color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme ._md-thumb { background-color: '{{background-50}}'; }md-switch.md-THEME_NAME-theme ._md-bar { background-color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked ._md-thumb { background-color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked ._md-bar { background-color: '{{accent-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-focused ._md-thumb:before { background-color: '{{accent-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-ink-ripple { color: '{{primary-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary ._md-thumb { background-color: '{{primary-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary ._md-bar { background-color: '{{primary-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary.md-focused ._md-thumb:before { background-color: '{{primary-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-ink-ripple { color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn ._md-thumb { background-color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn ._md-bar { background-color: '{{warn-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn.md-focused ._md-thumb:before { background-color: '{{warn-color-0.26}}'; }md-switch.md-THEME_NAME-theme[disabled] ._md-thumb { background-color: '{{background-400}}'; }md-switch.md-THEME_NAME-theme[disabled] ._md-bar { background-color: '{{foreground-4}}'; }md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: transparent; border-color: '{{foreground-4}}'; }md-tabs.md-THEME_NAME-theme .md-paginator md-icon { color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme md-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme .md-tab { color: '{{foreground-2}}'; } md-tabs.md-THEME_NAME-theme .md-tab[disabled], md-tabs.md-THEME_NAME-theme .md-tab[disabled] md-icon { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-active, md-tabs.md-THEME_NAME-theme .md-tab.md-active md-icon, md-tabs.md-THEME_NAME-theme .md-tab.md-focused, md-tabs.md-THEME_NAME-theme .md-tab.md-focused md-icon { color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-focused { background: '{{primary-color-0.1}}'; } md-tabs.md-THEME_NAME-theme .md-tab .md-ripple-container { color: '{{accent-A100}}'; }md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper { background-color: '{{accent-color}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{accent-A100}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{accent-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper { background-color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{primary-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{primary-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper { background-color: '{{warn-color}}'; } md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{warn-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{primary-color}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{primary-contrast}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{primary-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{accent-color}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{accent-A100}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{accent-contrast}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{warn-color}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{warn-contrast}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toast.md-THEME_NAME-theme .md-toast-content { background-color: #323232; color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button { color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight { color: '{{accent-color}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight.md-primary { color: '{{primary-color}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight.md-warn { color: '{{warn-color}}'; }md-tooltip.md-THEME_NAME-theme { color: '{{background-A100}}'; } md-tooltip.md-THEME_NAME-theme ._md-content { background-color: '{{foreground-2}}'; }md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) { background-color: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) md-icon { color: '{{primary-contrast}}'; fill: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) .md-button[disabled] md-icon { color: '{{primary-contrast-0.26}}'; fill: '{{primary-contrast-0.26}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent .md-ink-ripple { color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent md-icon { color: '{{accent-contrast}}'; fill: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent .md-button[disabled] md-icon { color: '{{accent-contrast-0.26}}'; fill: '{{accent-contrast-0.26}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-warn { background-color: '{{warn-color}}'; color: '{{warn-contrast}}'; }"); +}()}(window,window.angular),window.ngMaterial={version:{full:"1.1.0-rc.5"}}; \ No newline at end of file diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-sanitize/angular-sanitize.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-sanitize/angular-sanitize.min.js new file mode 100644 index 0000000000000000000000000000000000000000..48c46be8c14fb5cdcb9f8bffd7688c28f89922b3 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-sanitize/angular-sanitize.min.js @@ -0,0 +1,16 @@ +/* + AngularJS v1.5.9-build.5041+sha.ddb4ef1 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(s,g){'use strict';function H(g){var l=[];t(l,A).chars(g);return l.join("")}var B=g.$$minErr("$sanitize"),C,l,D,E,q,A,F,t;g.module("ngSanitize",[]).provider("$sanitize",function(){function k(a,e){var b={},c=a.split(","),h;for(h=0;h<c.length;h++)b[e?q(c[h]):c[h]]=!0;return b}function I(a){for(var e={},b=0,c=a.length;b<c;b++){var h=a[b];e[h.name]=h.value}return e}function G(a){return a.replace(/&/g,"&").replace(J,function(a){var b=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(b-55296)+ +(a-56320)+65536)+";"}).replace(K,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function x(a){for(;a;){if(a.nodeType===s.Node.ELEMENT_NODE)for(var e=a.attributes,b=0,c=e.length;b<c;b++){var h=e[b],d=h.name.toLowerCase();if("xmlns:ns1"===d||0===d.lastIndexOf("ns1:",0))a.removeAttributeNode(h),b--,c--}(e=a.firstChild)&&x(e);a=a.nextSibling}}var u=!1;this.$get=["$$sanitizeUri",function(a){u&&l(v,w);return function(e){var b=[];F(e,t(b,function(b,h){return!/^unsafe:/.test(a(b, +h))}));return b.join("")}}];this.enableSvg=function(a){return E(a)?(u=a,this):u};C=g.bind;l=g.extend;D=g.forEach;E=g.isDefined;q=g.lowercase;A=g.noop;F=function(a,e){null===a||void 0===a?a="":"string"!==typeof a&&(a=""+a);f.innerHTML=a;var b=5;do{if(0===b)throw B("uinput");b--;s.document.documentMode&&x(f);a=f.innerHTML;f.innerHTML=a}while(a!==f.innerHTML);for(b=f.firstChild;b;){switch(b.nodeType){case 1:e.start(b.nodeName.toLowerCase(),I(b.attributes));break;case 3:e.chars(b.textContent)}var c;if(!(c= +b.firstChild)&&(1===b.nodeType&&e.end(b.nodeName.toLowerCase()),c=b.nextSibling,!c))for(;null==c;){b=b.parentNode;if(b===f)break;c=b.nextSibling;1===b.nodeType&&e.end(b.nodeName.toLowerCase())}b=c}for(;b=f.firstChild;)f.removeChild(b)};t=function(a,e){var b=!1,c=C(a,a.push);return{start:function(a,d){a=q(a);!b&&z[a]&&(b=a);b||!0!==v[a]||(c("<"),c(a),D(d,function(b,d){var f=q(d),g="img"===a&&"src"===f||"background"===f;!0!==m[f]||!0===n[f]&&!e(b,g)||(c(" "),c(d),c('="'),c(G(b)),c('"'))}),c(">"))}, +end:function(a){a=q(a);b||!0!==v[a]||!0===y[a]||(c("</"),c(a),c(">"));a==b&&(b=!1)},chars:function(a){b||c(G(a))}}};var J=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,K=/([^#-~ |!])/g,y=k("area,br,col,hr,img,wbr"),d=k("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),r=k("rp,rt"),p=l({},r,d),d=l({},d,k("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),r=l({},r,k("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), +w=k("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),z=k("script,style"),v=l({},y,d,r,p),n=k("background,cite,href,longdesc,src,xlink:href"),p=k("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"), +r=k("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan", +!0),m=l({},n,r,p),f;(function(a){if(a.document&&a.document.implementation)a=a.document.implementation.createHTMLDocument("inert");else throw B("noinert");var e=(a.documentElement||a.getDocumentElement()).getElementsByTagName("body");1===e.length?f=e[0]:(e=a.createElement("html"),f=a.createElement("body"),e.appendChild(f),a.appendChild(e))})(s)});g.module("ngSanitize").filter("linky",["$sanitize",function(k){var l=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, +q=/^mailto:/i,x=g.$$minErr("linky"),u=g.isDefined,s=g.isFunction,t=g.isObject,y=g.isString;return function(d,g,p){function w(a){a&&m.push(H(a))}function z(a,b){var c,d=v(a);m.push("<a ");for(c in d)m.push(c+'="'+d[c]+'" ');!u(g)||"target"in d||m.push('target="',g,'" ');m.push('href="',a.replace(/"/g,"""),'">');w(b);m.push("</a>")}if(null==d||""===d)return d;if(!y(d))throw x("notstring",d);for(var v=s(p)?p:t(p)?function(){return p}:function(){return{}},n=d,m=[],f,a;d=n.match(l);)f=d[0],d[2]|| +d[4]||(f=(d[3]?"http://":"mailto:")+f),a=d.index,w(n.substr(0,a)),z(f,d[0].replace(q,"")),n=n.substring(a+d[0].length);w(n);return k(m.join(""))}}])})(window,window.angular); +//# sourceMappingURL=angular-sanitize.min.js.map diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.js index 570a6b5939afd27e5c261c621b1becddd6c1f35e..9d811b9e5fa116675a4156be34142b6ab97a6e93 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.js @@ -20,19 +20,45 @@ angular.module('ui.layout', []).controller('uiLayoutCtrl', [ var opts = angular.extend({}, $parse(tAttrs.uiLayout)(), $parse(tAttrs.options)()); var isUsingColumnFlow = opts.flow === 'column'; tElement.addClass('stretch').addClass('ui-layout-' + (opts.flow || 'row')); + var child_widths = []; + var total_width = tElement[0].clientWidth; for (_i = 0; _i < _child_len; ++_i) { angular.element(_childens[_i]).addClass('stretch'); + var init_width_attr = _childens[_i].attributes.getNamedItem('ui-layout-init-min-width'); + if(init_width_attr) { + if(init_width_attr.nodeValue.includes('%')) { + var child_width_perc = parseFloat(init_width_attr.nodeValue); + child_widths.push(child_width_perc); + } + else { + var child_width_perc = 100.0*parseFloat(init_width_attr.nodeValue) / total_width; + child_widths.push(child_width_perc); + } + } + else + child_widths.push(undefined); } + if (_child_len > 1) { + var totalDefinedChildWidth = child_widths.filter(function(cw) { return cw != undefined;}).reduce(function(a, b){return a+b;}); + var remainingWithForUndefinedChilds = 100 - totalDefinedChildWidth; + var numUndefinedChilds = child_widths.filter(function(cw) { return cw == undefined;}).length; + var undefinedChildWidth = remainingWithForUndefinedChilds / numUndefinedChilds; + var flowProperty = isUsingColumnFlow ? 'left' : 'top'; var oppositeFlowProperty = isUsingColumnFlow ? 'right' : 'bottom'; - var step = 100 / _child_len; + var prevPerc = 0; for (_i = 0; _i < _child_len; ++_i) { - var area = angular.element(_childens[_i]).css(flowProperty, step * _i + '%').css(oppositeFlowProperty, 100 - step * (_i + 1) + '%'); + var child_width = child_widths[_i]; + if(child_width == undefined) { + child_width = undefinedChildWidth; + } + var area = angular.element(_childens[_i]).css(flowProperty, prevPerc + '%').css(oppositeFlowProperty, 100 - (prevPerc + child_width) + '%'); if (_i < _child_len - 1) { - var bar = angular.element(splitBarElem_htmlTemplate).css(flowProperty, step * (_i + 1) + '%'); + var bar = angular.element(splitBarElem_htmlTemplate).css(flowProperty, prevPerc + child_width + '%'); area.after(bar); } + prevPerc += child_width; } } }, @@ -120,4 +146,4 @@ if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function (id) { clearTimeout(id); }; -} \ No newline at end of file +} diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.min.js index c6e5df5ccb63e3a42fc66a88384a535b8008e4b7..88ac76aa8bfd1b8450cac2647c4b58ebcf2ab573 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.min.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular-ui-layout/angular-ui-layout.min.js @@ -1,7 +1 @@ -/** - * angular-ui-layout - This directive allows you to split ! - * @version v0.0.0 - 2014-01-01 - * @link https://github.com/angular-ui/ui-layout - * @license MIT - */ -"use strict";angular.module("ui.layout",[]).controller("uiLayoutCtrl",["$scope","$attrs","$element",function(a,b,c){return{opts:angular.extend({},a.$eval(b.uiLayout),a.$eval(b.options)),element:c}}]).directive("uiLayout",["$parse",function(a){var b='<div class="stretch ui-splitbar"></div>';return{restrict:"AE",compile:function(c,d){var e,f=c.children(),g=f.length,h=angular.extend({},a(d.uiLayout)(),a(d.options)()),i="column"===h.flow;for(c.addClass("stretch").addClass("ui-layout-"+(h.flow||"row")),e=0;g>e;++e)angular.element(f[e]).addClass("stretch");if(g>1){var j=i?"left":"top",k=i?"right":"bottom",l=100/g;for(e=0;g>e;++e){var m=angular.element(f[e]).css(j,l*e+"%").css(k,100-l*(e+1)+"%");if(g-1>e){var n=angular.element(b).css(j,l*(e+1)+"%");m.after(n)}}}},controller:"uiLayoutCtrl"}}]).directive("uiSplitbar",function(){var a=angular.element(document.body.parentElement);return{require:"^uiLayout",restrict:"EAC",link:function(b,c,d,e){function f(){var a=e.element[0].getBoundingClientRect(),b=q.getBoundingClientRect();k.time=+new Date,k.barSize=b[p],k.layoutSize=a[p],k.layoutOrigine=a[n]}function g(){var a=(j-k.layoutOrigine)/k.layoutSize*100;a=Math.min(a,100-k.barSize/k.layoutSize*100),a=Math.max(a,parseInt(q.previousElementSibling.style[n],10)),q.nextElementSibling.nextElementSibling&&(a=Math.min(a,parseInt(q.nextElementSibling.nextElementSibling.style[n],10))),q.style[n]=q.nextElementSibling.style[n]=a+"%",q.previousElementSibling.style[o]=100-a+"%",i=null}function h(a){j=a[m]||a.originalEvent[m],i&&window.cancelAnimationFrame(i),(!k.time||+new Date>k.time+1e3)&&f(),i=window.requestAnimationFrame(g)}var i,j,k={},l="column"===e.opts.flow,m=l?"clientX":"clientY",n=l?"left":"top",o=l?"right":"bottom",p=l?"width":"height",q=c[0];c.on("mousedown touchstart",function(b){return b.preventDefault(),b.stopPropagation(),a.on("mousemove touchmove",h),!1}),a.on("mouseup touchend",function(){a.off("mousemove touchmove")})}}});for(var lastTime=0,vendors=["ms","moz","webkit","o"],x=0;x<vendors.length&&!window.requestAnimationFrame;++x)window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(a){var b=(new Date).getTime(),c=Math.max(0,16-(b-lastTime)),d=window.setTimeout(function(){a(b+c)},c);return lastTime=b+c,d}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)}); \ No newline at end of file +"use strict";angular.module("ui.layout",[]).controller("uiLayoutCtrl",["$scope","$attrs","$element",function(b,c,d){return{opts:angular.extend({},b.$eval(c.uiLayout),b.$eval(c.options)),element:d}}]).directive("uiLayout",["$parse",function(a){var b='<div class="stretch ui-splitbar"></div>';return{restrict:"AE",compile:function(d,e){var f,g=d.children(),h=g.length,i=angular.extend({},a(e.uiLayout)(),a(e.options)()),j="column"===i.flow;d.addClass("stretch").addClass("ui-layout-"+(i.flow||"row"));var k=[],l=d[0].clientWidth;for(f=0;f<h;++f){angular.element(g[f]).addClass("stretch");var m=g[f].attributes.getNamedItem("ui-layout-init-min-width");if(m)if(m.nodeValue.includes("%")){var n=parseFloat(m.nodeValue);k.push(n)}else{var n=100*parseFloat(m.nodeValue)/l;k.push(n)}else k.push(void 0)}if(h>1){var o=k.filter(function(a){return void 0!=a}).reduce(function(a,b){return a+b}),p=100-o,q=k.filter(function(a){return void 0==a}).length,r=p/q,s=j?"left":"top",t=j?"right":"bottom",u=0;for(f=0;f<h;++f){var v=k[f];void 0==v&&(v=r);var w=angular.element(g[f]).css(s,u+"%").css(t,100-(u+v)+"%");if(f<h-1){var x=angular.element(b).css(s,u+v+"%");w.after(x)}u+=v}}},controller:"uiLayoutCtrl"}}]).directive("uiSplitbar",function(){var a=angular.element(document.body.parentElement);return{require:"^uiLayout",restrict:"EAC",link:function(b,c,d,e){function o(){var a=e.element[0].getBoundingClientRect(),b=n.getBoundingClientRect();h.time=+new Date,h.barSize=b[m],h.layoutSize=a[m],h.layoutOrigine=a[k]}function p(){var a=(g-h.layoutOrigine)/h.layoutSize*100;a=Math.min(a,100-h.barSize/h.layoutSize*100),a=Math.max(a,parseInt(n.previousElementSibling.style[k],10)),n.nextElementSibling.nextElementSibling&&(a=Math.min(a,parseInt(n.nextElementSibling.nextElementSibling.style[k],10))),n.style[k]=n.nextElementSibling.style[k]=a+"%",n.previousElementSibling.style[l]=100-a+"%",f=null}function q(a){g=a[j]||a.originalEvent[j],f&&window.cancelAnimationFrame(f),(!h.time||+new Date>h.time+1e3)&&o(),f=window.requestAnimationFrame(p)}var f,g,h={},i="column"===e.opts.flow,j=i?"clientX":"clientY",k=i?"left":"top",l=i?"right":"bottom",m=i?"width":"height",n=c[0];c.on("mousedown touchstart",function(b){return b.preventDefault(),b.stopPropagation(),a.on("mousemove touchmove",q),!1}),a.on("mouseup touchend",function(){a.off("mousemove touchmove")})}}});for(var lastTime=0,vendors=["ms","moz","webkit","o"],x=0;x<vendors.length&&!window.requestAnimationFrame;++x)window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(a){var b=(new Date).getTime(),c=Math.max(0,16-(b-lastTime)),d=window.setTimeout(function(){a(b+c)},c);return lastTime=b+c,d}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)}); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.js deleted file mode 100644 index 34a93c1c41ac865b6e080957ce8081c92e90d7f1..0000000000000000000000000000000000000000 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.js +++ /dev/null @@ -1,28904 +0,0 @@ -/** - * @license AngularJS v1.4.7 - * (c) 2010-2015 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, document, undefined) {'use strict'; - -/** - * @description - * - * This object provides a utility for producing rich Error messages within - * Angular. It can be called as follows: - * - * var exampleMinErr = minErr('example'); - * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); - * - * The above creates an instance of minErr in the example namespace. The - * resulting error will have a namespaced error code of example.one. The - * resulting error will replace {0} with the value of foo, and {1} with the - * value of bar. The object is not restricted in the number of arguments it can - * take. - * - * If fewer arguments are specified than necessary for interpolation, the extra - * interpolation markers will be preserved in the final string. - * - * Since data will be parsed statically during a build step, some restrictions - * are applied with respect to how minErr instances are created and called. - * Instances should have names of the form namespaceMinErr for a minErr created - * using minErr('namespace') . Error codes, namespaces and template strings - * should all be static strings, not variables or general expressions. - * - * @param {string} module The namespace to use for the new minErr instance. - * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning - * error from returned function, for cases when a particular type of error is useful. - * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance - */ - -function minErr(module, ErrorConstructor) { - ErrorConstructor = ErrorConstructor || Error; - return function() { - var SKIP_INDEXES = 2; - - var templateArgs = arguments, - code = templateArgs[0], - message = '[' + (module ? module + ':' : '') + code + '] ', - template = templateArgs[1], - paramPrefix, i; - - message += template.replace(/\{\d+\}/g, function(match) { - var index = +match.slice(1, -1), - shiftedIndex = index + SKIP_INDEXES; - - if (shiftedIndex < templateArgs.length) { - return toDebugString(templateArgs[shiftedIndex]); - } - - return match; - }); - - message += '\nhttp://errors.angularjs.org/1.4.7/' + - (module ? module + '/' : '') + code; - - for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { - message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + - encodeURIComponent(toDebugString(templateArgs[i])); - } - - return new ErrorConstructor(message); - }; -} - -/* We need to tell jshint what variables are being exported */ -/* global angular: true, - msie: true, - jqLite: true, - jQuery: true, - slice: true, - splice: true, - push: true, - toString: true, - ngMinErr: true, - angularModule: true, - uid: true, - REGEX_STRING_REGEXP: true, - VALIDITY_STATE_PROPERTY: true, - - lowercase: true, - uppercase: true, - manualLowercase: true, - manualUppercase: true, - nodeName_: true, - isArrayLike: true, - forEach: true, - forEachSorted: true, - reverseParams: true, - nextUid: true, - setHashKey: true, - extend: true, - toInt: true, - inherit: true, - merge: true, - noop: true, - identity: true, - valueFn: true, - isUndefined: true, - isDefined: true, - isObject: true, - isBlankObject: true, - isString: true, - isNumber: true, - isDate: true, - isArray: true, - isFunction: true, - isRegExp: true, - isWindow: true, - isScope: true, - isFile: true, - isFormData: true, - isBlob: true, - isBoolean: true, - isPromiseLike: true, - trim: true, - escapeForRegexp: true, - isElement: true, - makeMap: true, - includes: true, - arrayRemove: true, - copy: true, - shallowCopy: true, - equals: true, - csp: true, - jq: true, - concat: true, - sliceArgs: true, - bind: true, - toJsonReplacer: true, - toJson: true, - fromJson: true, - convertTimezoneToLocal: true, - timezoneToOffset: true, - startingTag: true, - tryDecodeURIComponent: true, - parseKeyValue: true, - toKeyValue: true, - encodeUriSegment: true, - encodeUriQuery: true, - angularInit: true, - bootstrap: true, - getTestability: true, - snake_case: true, - bindJQuery: true, - assertArg: true, - assertArgFn: true, - assertNotHasOwnProperty: true, - getter: true, - getBlockNodes: true, - hasOwnProperty: true, - createMap: true, - - NODE_TYPE_ELEMENT: true, - NODE_TYPE_ATTRIBUTE: true, - NODE_TYPE_TEXT: true, - NODE_TYPE_COMMENT: true, - NODE_TYPE_DOCUMENT: true, - NODE_TYPE_DOCUMENT_FRAGMENT: true, -*/ - -//////////////////////////////////// - -/** - * @ngdoc module - * @name ng - * @module ng - * @description - * - * # ng (core module) - * The ng module is loaded by default when an AngularJS application is started. The module itself - * contains the essential components for an AngularJS application to function. The table below - * lists a high level breakdown of each of the services/factories, filters, directives and testing - * components available within this core module. - * - * <div doc-module-components="ng"></div> - */ - -var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; - -// The name of a form control's ValidityState property. -// This is used so that it's possible for internal tests to create mock ValidityStates. -var VALIDITY_STATE_PROPERTY = 'validity'; - -/** - * @ngdoc function - * @name angular.lowercase - * @module ng - * @kind function - * - * @description Converts the specified string to lowercase. - * @param {string} string String to be converted to lowercase. - * @returns {string} Lowercased string. - */ -var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; -var hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * @ngdoc function - * @name angular.uppercase - * @module ng - * @kind function - * - * @description Converts the specified string to uppercase. - * @param {string} string String to be converted to uppercase. - * @returns {string} Uppercased string. - */ -var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; - - -var manualLowercase = function(s) { - /* jshint bitwise: false */ - return isString(s) - ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) - : s; -}; -var manualUppercase = function(s) { - /* jshint bitwise: false */ - return isString(s) - ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) - : s; -}; - - -// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish -// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods -// with correct but slower alternatives. -if ('i' !== 'I'.toLowerCase()) { - lowercase = manualLowercase; - uppercase = manualUppercase; -} - - -var - msie, // holds major version number for IE, or NaN if UA is not IE. - jqLite, // delay binding since jQuery could be loaded after us. - jQuery, // delay binding - slice = [].slice, - splice = [].splice, - push = [].push, - toString = Object.prototype.toString, - getPrototypeOf = Object.getPrototypeOf, - ngMinErr = minErr('ng'), - - /** @name angular */ - angular = window.angular || (window.angular = {}), - angularModule, - uid = 0; - -/** - * documentMode is an IE-only property - * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx - */ -msie = document.documentMode; - - -/** - * @private - * @param {*} obj - * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, - * String ...) - */ -function isArrayLike(obj) { - if (obj == null || isWindow(obj)) { - return false; - } - - // Support: iOS 8.2 (not reproducible in simulator) - // "length" in obj used to prevent JIT error (gh-11508) - var length = "length" in Object(obj) && obj.length; - - if (obj.nodeType === NODE_TYPE_ELEMENT && length) { - return true; - } - - return isString(obj) || isArray(obj) || length === 0 || - typeof length === 'number' && length > 0 && (length - 1) in obj; -} - -/** - * @ngdoc function - * @name angular.forEach - * @module ng - * @kind function - * - * @description - * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` - * is the value of an object property or an array element, `key` is the object property key or - * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. - * - * It is worth noting that `.forEach` does not iterate over inherited properties because it filters - * using the `hasOwnProperty` method. - * - * Unlike ES262's - * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), - * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just - * return the value provided. - * - ```js - var values = {name: 'misko', gender: 'male'}; - var log = []; - angular.forEach(values, function(value, key) { - this.push(key + ': ' + value); - }, log); - expect(log).toEqual(['name: misko', 'gender: male']); - ``` - * - * @param {Object|Array} obj Object to iterate over. - * @param {Function} iterator Iterator function. - * @param {Object=} context Object to become context (`this`) for the iterator function. - * @returns {Object|Array} Reference to `obj`. - */ - -function forEach(obj, iterator, context) { - var key, length; - if (obj) { - if (isFunction(obj)) { - for (key in obj) { - // Need to check if hasOwnProperty exists, - // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function - if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { - iterator.call(context, obj[key], key, obj); - } - } - } else if (isArray(obj) || isArrayLike(obj)) { - var isPrimitive = typeof obj !== 'object'; - for (key = 0, length = obj.length; key < length; key++) { - if (isPrimitive || key in obj) { - iterator.call(context, obj[key], key, obj); - } - } - } else if (obj.forEach && obj.forEach !== forEach) { - obj.forEach(iterator, context, obj); - } else if (isBlankObject(obj)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in obj) { - iterator.call(context, obj[key], key, obj); - } - } else if (typeof obj.hasOwnProperty === 'function') { - // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key, obj); - } - } - } else { - // Slow path for objects which do not have a method `hasOwnProperty` - for (key in obj) { - if (hasOwnProperty.call(obj, key)) { - iterator.call(context, obj[key], key, obj); - } - } - } - } - return obj; -} - -function forEachSorted(obj, iterator, context) { - var keys = Object.keys(obj).sort(); - for (var i = 0; i < keys.length; i++) { - iterator.call(context, obj[keys[i]], keys[i]); - } - return keys; -} - - -/** - * when using forEach the params are value, key, but it is often useful to have key, value. - * @param {function(string, *)} iteratorFn - * @returns {function(*, string)} - */ -function reverseParams(iteratorFn) { - return function(value, key) { iteratorFn(key, value); }; -} - -/** - * A consistent way of creating unique IDs in angular. - * - * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before - * we hit number precision issues in JavaScript. - * - * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M - * - * @returns {number} an unique alpha-numeric string - */ -function nextUid() { - return ++uid; -} - - -/** - * Set or clear the hashkey for an object. - * @param obj object - * @param h the hashkey (!truthy to delete the hashkey) - */ -function setHashKey(obj, h) { - if (h) { - obj.$$hashKey = h; - } else { - delete obj.$$hashKey; - } -} - - -function baseExtend(dst, objs, deep) { - var h = dst.$$hashKey; - - for (var i = 0, ii = objs.length; i < ii; ++i) { - var obj = objs[i]; - if (!isObject(obj) && !isFunction(obj)) continue; - var keys = Object.keys(obj); - for (var j = 0, jj = keys.length; j < jj; j++) { - var key = keys[j]; - var src = obj[key]; - - if (deep && isObject(src)) { - if (isDate(src)) { - dst[key] = new Date(src.valueOf()); - } else if (isRegExp(src)) { - dst[key] = new RegExp(src); - } else { - if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; - baseExtend(dst[key], [src], true); - } - } else { - dst[key] = src; - } - } - } - - setHashKey(dst, h); - return dst; -} - -/** - * @ngdoc function - * @name angular.extend - * @module ng - * @kind function - * - * @description - * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) - * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so - * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. - * - * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use - * {@link angular.merge} for this. - * - * @param {Object} dst Destination object. - * @param {...Object} src Source object(s). - * @returns {Object} Reference to `dst`. - */ -function extend(dst) { - return baseExtend(dst, slice.call(arguments, 1), false); -} - - -/** -* @ngdoc function -* @name angular.merge -* @module ng -* @kind function -* -* @description -* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) -* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so -* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. -* -* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source -* objects, performing a deep copy. -* -* @param {Object} dst Destination object. -* @param {...Object} src Source object(s). -* @returns {Object} Reference to `dst`. -*/ -function merge(dst) { - return baseExtend(dst, slice.call(arguments, 1), true); -} - - - -function toInt(str) { - return parseInt(str, 10); -} - - -function inherit(parent, extra) { - return extend(Object.create(parent), extra); -} - -/** - * @ngdoc function - * @name angular.noop - * @module ng - * @kind function - * - * @description - * A function that performs no operations. This function can be useful when writing code in the - * functional style. - ```js - function foo(callback) { - var result = calculateResult(); - (callback || angular.noop)(result); - } - ``` - */ -function noop() {} -noop.$inject = []; - - -/** - * @ngdoc function - * @name angular.identity - * @module ng - * @kind function - * - * @description - * A function that returns its first argument. This function is useful when writing code in the - * functional style. - * - ```js - function transformer(transformationFn, value) { - return (transformationFn || angular.identity)(value); - }; - ``` - * @param {*} value to be returned. - * @returns {*} the value passed in. - */ -function identity($) {return $;} -identity.$inject = []; - - -function valueFn(value) {return function() {return value;};} - -function hasCustomToString(obj) { - return isFunction(obj.toString) && obj.toString !== Object.prototype.toString; -} - - -/** - * @ngdoc function - * @name angular.isUndefined - * @module ng - * @kind function - * - * @description - * Determines if a reference is undefined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is undefined. - */ -function isUndefined(value) {return typeof value === 'undefined';} - - -/** - * @ngdoc function - * @name angular.isDefined - * @module ng - * @kind function - * - * @description - * Determines if a reference is defined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is defined. - */ -function isDefined(value) {return typeof value !== 'undefined';} - - -/** - * @ngdoc function - * @name angular.isObject - * @module ng - * @kind function - * - * @description - * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not - * considered to be objects. Note that JavaScript arrays are objects. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Object` but not `null`. - */ -function isObject(value) { - // http://jsperf.com/isobject4 - return value !== null && typeof value === 'object'; -} - - -/** - * Determine if a value is an object with a null prototype - * - * @returns {boolean} True if `value` is an `Object` with a null prototype - */ -function isBlankObject(value) { - return value !== null && typeof value === 'object' && !getPrototypeOf(value); -} - - -/** - * @ngdoc function - * @name angular.isString - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `String`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `String`. - */ -function isString(value) {return typeof value === 'string';} - - -/** - * @ngdoc function - * @name angular.isNumber - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `Number`. - * - * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. - * - * If you wish to exclude these then you can use the native - * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) - * method. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Number`. - */ -function isNumber(value) {return typeof value === 'number';} - - -/** - * @ngdoc function - * @name angular.isDate - * @module ng - * @kind function - * - * @description - * Determines if a value is a date. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Date`. - */ -function isDate(value) { - return toString.call(value) === '[object Date]'; -} - - -/** - * @ngdoc function - * @name angular.isArray - * @module ng - * @kind function - * - * @description - * Determines if a reference is an `Array`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Array`. - */ -var isArray = Array.isArray; - -/** - * @ngdoc function - * @name angular.isFunction - * @module ng - * @kind function - * - * @description - * Determines if a reference is a `Function`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Function`. - */ -function isFunction(value) {return typeof value === 'function';} - - -/** - * Determines if a value is a regular expression object. - * - * @private - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `RegExp`. - */ -function isRegExp(value) { - return toString.call(value) === '[object RegExp]'; -} - - -/** - * Checks if `obj` is a window object. - * - * @private - * @param {*} obj Object to check - * @returns {boolean} True if `obj` is a window obj. - */ -function isWindow(obj) { - return obj && obj.window === obj; -} - - -function isScope(obj) { - return obj && obj.$evalAsync && obj.$watch; -} - - -function isFile(obj) { - return toString.call(obj) === '[object File]'; -} - - -function isFormData(obj) { - return toString.call(obj) === '[object FormData]'; -} - - -function isBlob(obj) { - return toString.call(obj) === '[object Blob]'; -} - - -function isBoolean(value) { - return typeof value === 'boolean'; -} - - -function isPromiseLike(obj) { - return obj && isFunction(obj.then); -} - - -var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/; -function isTypedArray(value) { - return TYPED_ARRAY_REGEXP.test(toString.call(value)); -} - - -var trim = function(value) { - return isString(value) ? value.trim() : value; -}; - -// Copied from: -// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 -// Prereq: s is a string. -var escapeForRegexp = function(s) { - return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). - replace(/\x08/g, '\\x08'); -}; - - -/** - * @ngdoc function - * @name angular.isElement - * @module ng - * @kind function - * - * @description - * Determines if a reference is a DOM element (or wrapped jQuery element). - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). - */ -function isElement(node) { - return !!(node && - (node.nodeName // we are a direct element - || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API -} - -/** - * @param str 'key1,key2,...' - * @returns {object} in the form of {key1:true, key2:true, ...} - */ -function makeMap(str) { - var obj = {}, items = str.split(","), i; - for (i = 0; i < items.length; i++) { - obj[items[i]] = true; - } - return obj; -} - - -function nodeName_(element) { - return lowercase(element.nodeName || (element[0] && element[0].nodeName)); -} - -function includes(array, obj) { - return Array.prototype.indexOf.call(array, obj) != -1; -} - -function arrayRemove(array, value) { - var index = array.indexOf(value); - if (index >= 0) { - array.splice(index, 1); - } - return index; -} - -/** - * @ngdoc function - * @name angular.copy - * @module ng - * @kind function - * - * @description - * Creates a deep copy of `source`, which should be an object or an array. - * - * * If no destination is supplied, a copy of the object or array is created. - * * If a destination is provided, all of its elements (for arrays) or properties (for objects) - * are deleted and then all elements/properties from the source are copied to it. - * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. - * * If `source` is identical to 'destination' an exception will be thrown. - * - * @param {*} source The source that will be used to make a copy. - * Can be any type, including primitives, `null`, and `undefined`. - * @param {(Object|Array)=} destination Destination into which the source is copied. If - * provided, must be of the same type as `source`. - * @returns {*} The copy or updated `destination`, if `destination` was specified. - * - * @example - <example module="copyExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <form novalidate class="simple-form"> - Name: <input type="text" ng-model="user.name" /><br /> - E-mail: <input type="email" ng-model="user.email" /><br /> - Gender: <input type="radio" ng-model="user.gender" value="male" />male - <input type="radio" ng-model="user.gender" value="female" />female<br /> - <button ng-click="reset()">RESET</button> - <button ng-click="update(user)">SAVE</button> - </form> - <pre>form = {{user | json}}</pre> - <pre>master = {{master | json}}</pre> - </div> - - <script> - angular.module('copyExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.master= {}; - - $scope.update = function(user) { - // Example with 1 argument - $scope.master= angular.copy(user); - }; - - $scope.reset = function() { - // Example with 2 arguments - angular.copy($scope.master, $scope.user); - }; - - $scope.reset(); - }]); - </script> - </file> - </example> - */ -function copy(source, destination, stackSource, stackDest) { - if (isWindow(source) || isScope(source)) { - throw ngMinErr('cpws', - "Can't copy! Making copies of Window or Scope instances is not supported."); - } - if (isTypedArray(destination)) { - throw ngMinErr('cpta', - "Can't copy! TypedArray destination cannot be mutated."); - } - - if (!destination) { - destination = source; - if (isObject(source)) { - var index; - if (stackSource && (index = stackSource.indexOf(source)) !== -1) { - return stackDest[index]; - } - - // TypedArray, Date and RegExp have specific copy functionality and must be - // pushed onto the stack before returning. - // Array and other objects create the base object and recurse to copy child - // objects. The array/object will be pushed onto the stack when recursed. - if (isArray(source)) { - return copy(source, [], stackSource, stackDest); - } else if (isTypedArray(source)) { - destination = new source.constructor(source); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isRegExp(source)) { - destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); - destination.lastIndex = source.lastIndex; - } else if (isFunction(source.cloneNode)) { - destination = source.cloneNode(true); - } else { - var emptyObject = Object.create(getPrototypeOf(source)); - return copy(source, emptyObject, stackSource, stackDest); - } - - if (stackDest) { - stackSource.push(source); - stackDest.push(destination); - } - } - } else { - if (source === destination) throw ngMinErr('cpi', - "Can't copy! Source and destination are identical."); - - stackSource = stackSource || []; - stackDest = stackDest || []; - - if (isObject(source)) { - stackSource.push(source); - stackDest.push(destination); - } - - var result, key; - if (isArray(source)) { - destination.length = 0; - for (var i = 0; i < source.length; i++) { - destination.push(copy(source[i], null, stackSource, stackDest)); - } - } else { - var h = destination.$$hashKey; - if (isArray(destination)) { - destination.length = 0; - } else { - forEach(destination, function(value, key) { - delete destination[key]; - }); - } - if (isBlankObject(source)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in source) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } else if (source && typeof source.hasOwnProperty === 'function') { - // Slow path, which must rely on hasOwnProperty - for (key in source) { - if (source.hasOwnProperty(key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } - } else { - // Slowest path --- hasOwnProperty can't be called as a method - for (key in source) { - if (hasOwnProperty.call(source, key)) { - destination[key] = copy(source[key], null, stackSource, stackDest); - } - } - } - setHashKey(destination,h); - } - } - return destination; -} - -/** - * Creates a shallow copy of an object, an array or a primitive. - * - * Assumes that there are no proto properties for objects. - */ -function shallowCopy(src, dst) { - if (isArray(src)) { - dst = dst || []; - - for (var i = 0, ii = src.length; i < ii; i++) { - dst[i] = src[i]; - } - } else if (isObject(src)) { - dst = dst || {}; - - for (var key in src) { - if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { - dst[key] = src[key]; - } - } - } - - return dst || src; -} - - -/** - * @ngdoc function - * @name angular.equals - * @module ng - * @kind function - * - * @description - * Determines if two objects or two values are equivalent. Supports value types, regular - * expressions, arrays and objects. - * - * Two objects or values are considered equivalent if at least one of the following is true: - * - * * Both objects or values pass `===` comparison. - * * Both objects or values are of the same type and all of their properties are equal by - * comparing them with `angular.equals`. - * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) - * * Both values represent the same regular expression (In JavaScript, - * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual - * representation matches). - * - * During a property comparison, properties of `function` type and properties with names - * that begin with `$` are ignored. - * - * Scope and DOMWindow objects are being compared only by identify (`===`). - * - * @param {*} o1 Object or value to compare. - * @param {*} o2 Object or value to compare. - * @returns {boolean} True if arguments are equal. - */ -function equals(o1, o2) { - if (o1 === o2) return true; - if (o1 === null || o2 === null) return false; - if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN - var t1 = typeof o1, t2 = typeof o2, length, key, keySet; - if (t1 == t2) { - if (t1 == 'object') { - if (isArray(o1)) { - if (!isArray(o2)) return false; - if ((length = o1.length) == o2.length) { - for (key = 0; key < length; key++) { - if (!equals(o1[key], o2[key])) return false; - } - return true; - } - } else if (isDate(o1)) { - if (!isDate(o2)) return false; - return equals(o1.getTime(), o2.getTime()); - } else if (isRegExp(o1)) { - return isRegExp(o2) ? o1.toString() == o2.toString() : false; - } else { - if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || - isArray(o2) || isDate(o2) || isRegExp(o2)) return false; - keySet = createMap(); - for (key in o1) { - if (key.charAt(0) === '$' || isFunction(o1[key])) continue; - if (!equals(o1[key], o2[key])) return false; - keySet[key] = true; - } - for (key in o2) { - if (!(key in keySet) && - key.charAt(0) !== '$' && - isDefined(o2[key]) && - !isFunction(o2[key])) return false; - } - return true; - } - } - } - return false; -} - -var csp = function() { - if (!isDefined(csp.rules)) { - - - var ngCspElement = (document.querySelector('[ng-csp]') || - document.querySelector('[data-ng-csp]')); - - if (ngCspElement) { - var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || - ngCspElement.getAttribute('data-ng-csp'); - csp.rules = { - noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), - noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) - }; - } else { - csp.rules = { - noUnsafeEval: noUnsafeEval(), - noInlineStyle: false - }; - } - } - - return csp.rules; - - function noUnsafeEval() { - try { - /* jshint -W031, -W054 */ - new Function(''); - /* jshint +W031, +W054 */ - return false; - } catch (e) { - return true; - } - } -}; - -/** - * @ngdoc directive - * @module ng - * @name ngJq - * - * @element ANY - * @param {string=} ngJq the name of the library available under `window` - * to be used for angular.element - * @description - * Use this directive to force the angular.element library. This should be - * used to force either jqLite by leaving ng-jq blank or setting the name of - * the jquery variable under window (eg. jQuery). - * - * Since angular looks for this directive when it is loaded (doesn't wait for the - * DOMContentLoaded event), it must be placed on an element that comes before the script - * which loads angular. Also, only the first instance of `ng-jq` will be used and all - * others ignored. - * - * @example - * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. - ```html - <!doctype html> - <html ng-app ng-jq> - ... - ... - </html> - ``` - * @example - * This example shows how to use a jQuery based library of a different name. - * The library name must be available at the top most 'window'. - ```html - <!doctype html> - <html ng-app ng-jq="jQueryLib"> - ... - ... - </html> - ``` - */ -var jq = function() { - if (isDefined(jq.name_)) return jq.name_; - var el; - var i, ii = ngAttrPrefixes.length, prefix, name; - for (i = 0; i < ii; ++i) { - prefix = ngAttrPrefixes[i]; - if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) { - name = el.getAttribute(prefix + 'jq'); - break; - } - } - - return (jq.name_ = name); -}; - -function concat(array1, array2, index) { - return array1.concat(slice.call(array2, index)); -} - -function sliceArgs(args, startIndex) { - return slice.call(args, startIndex || 0); -} - - -/* jshint -W101 */ -/** - * @ngdoc function - * @name angular.bind - * @module ng - * @kind function - * - * @description - * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for - * `fn`). You can supply optional `args` that are prebound to the function. This feature is also - * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as - * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). - * - * @param {Object} self Context which `fn` should be evaluated in. - * @param {function()} fn Function to be bound. - * @param {...*} args Optional arguments to be prebound to the `fn` function call. - * @returns {function()} Function that wraps the `fn` with all the specified bindings. - */ -/* jshint +W101 */ -function bind(self, fn) { - var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; - if (isFunction(fn) && !(fn instanceof RegExp)) { - return curryArgs.length - ? function() { - return arguments.length - ? fn.apply(self, concat(curryArgs, arguments, 0)) - : fn.apply(self, curryArgs); - } - : function() { - return arguments.length - ? fn.apply(self, arguments) - : fn.call(self); - }; - } else { - // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) - return fn; - } -} - - -function toJsonReplacer(key, value) { - var val = value; - - if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { - val = undefined; - } else if (isWindow(value)) { - val = '$WINDOW'; - } else if (value && document === value) { - val = '$DOCUMENT'; - } else if (isScope(value)) { - val = '$SCOPE'; - } - - return val; -} - - -/** - * @ngdoc function - * @name angular.toJson - * @module ng - * @kind function - * - * @description - * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be - * stripped since angular uses this notation internally. - * - * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. - * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. - * If set to an integer, the JSON output will contain that many spaces per indentation. - * @returns {string|undefined} JSON-ified string representing `obj`. - */ -function toJson(obj, pretty) { - if (typeof obj === 'undefined') return undefined; - if (!isNumber(pretty)) { - pretty = pretty ? 2 : null; - } - return JSON.stringify(obj, toJsonReplacer, pretty); -} - - -/** - * @ngdoc function - * @name angular.fromJson - * @module ng - * @kind function - * - * @description - * Deserializes a JSON string. - * - * @param {string} json JSON string to deserialize. - * @returns {Object|Array|string|number} Deserialized JSON string. - */ -function fromJson(json) { - return isString(json) - ? JSON.parse(json) - : json; -} - - -function timezoneToOffset(timezone, fallback) { - var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; - return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; -} - - -function addDateMinutes(date, minutes) { - date = new Date(date.getTime()); - date.setMinutes(date.getMinutes() + minutes); - return date; -} - - -function convertTimezoneToLocal(date, timezone, reverse) { - reverse = reverse ? -1 : 1; - var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); - return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset())); -} - - -/** - * @returns {string} Returns the string representation of the element. - */ -function startingTag(element) { - element = jqLite(element).clone(); - try { - // turns out IE does not let you set .html() on elements which - // are not allowed to have children. So we just ignore it. - element.empty(); - } catch (e) {} - var elemHtml = jqLite('<div>').append(element).html(); - try { - return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : - elemHtml. - match(/^(<[^>]+>)/)[1]. - replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); - } catch (e) { - return lowercase(elemHtml); - } - -} - - -///////////////////////////////////////////////// - -/** - * Tries to decode the URI component without throwing an exception. - * - * @private - * @param str value potential URI component to check. - * @returns {boolean} True if `value` can be decoded - * with the decodeURIComponent function. - */ -function tryDecodeURIComponent(value) { - try { - return decodeURIComponent(value); - } catch (e) { - // Ignore any invalid uri component - } -} - - -/** - * Parses an escaped url query string into key-value pairs. - * @returns {Object.<string,boolean|Array>} - */ -function parseKeyValue(/**string*/keyValue) { - var obj = {}; - forEach((keyValue || "").split('&'), function(keyValue) { - var splitPoint, key, val; - if (keyValue) { - key = keyValue = keyValue.replace(/\+/g,'%20'); - splitPoint = keyValue.indexOf('='); - if (splitPoint !== -1) { - key = keyValue.substring(0, splitPoint); - val = keyValue.substring(splitPoint + 1); - } - key = tryDecodeURIComponent(key); - if (isDefined(key)) { - val = isDefined(val) ? tryDecodeURIComponent(val) : true; - if (!hasOwnProperty.call(obj, key)) { - obj[key] = val; - } else if (isArray(obj[key])) { - obj[key].push(val); - } else { - obj[key] = [obj[key],val]; - } - } - } - }); - return obj; -} - -function toKeyValue(obj) { - var parts = []; - forEach(obj, function(value, key) { - if (isArray(value)) { - forEach(value, function(arrayValue) { - parts.push(encodeUriQuery(key, true) + - (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); - }); - } else { - parts.push(encodeUriQuery(key, true) + - (value === true ? '' : '=' + encodeUriQuery(value, true))); - } - }); - return parts.length ? parts.join('&') : ''; -} - - -/** - * We need our custom method because encodeURIComponent is too aggressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); -} - - -/** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace(/%3B/gi, ';'). - replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); -} - -var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; - -function getNgAttribute(element, ngAttr) { - var attr, i, ii = ngAttrPrefixes.length; - for (i = 0; i < ii; ++i) { - attr = ngAttrPrefixes[i] + ngAttr; - if (isString(attr = element.getAttribute(attr))) { - return attr; - } - } - return null; -} - -/** - * @ngdoc directive - * @name ngApp - * @module ng - * - * @element ANY - * @param {angular.Module} ngApp an optional application - * {@link angular.module module} name to load. - * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be - * created in "strict-di" mode. This means that the application will fail to invoke functions which - * do not use explicit function annotation (and are thus unsuitable for minification), as described - * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in - * tracking down the root of these bugs. - * - * @description - * - * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive - * designates the **root element** of the application and is typically placed near the root element - * of the page - e.g. on the `<body>` or `<html>` tags. - * - * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` - * found in the document will be used to define the root element to auto-bootstrap as an - * application. To run multiple applications in an HTML document you must manually bootstrap them using - * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. - * - * You can specify an **AngularJS module** to be used as the root module for the application. This - * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It - * should contain the application code needed or have dependencies on other modules that will - * contain the code. See {@link angular.module} for more information. - * - * In the example below if the `ngApp` directive were not placed on the `html` element then the - * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` - * would not be resolved to `3`. - * - * `ngApp` is the easiest, and most common way to bootstrap an application. - * - <example module="ngAppDemo"> - <file name="index.html"> - <div ng-controller="ngAppDemoController"> - I can add: {{a}} + {{b}} = {{ a+b }} - </div> - </file> - <file name="script.js"> - angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { - $scope.a = 1; - $scope.b = 2; - }); - </file> - </example> - * - * Using `ngStrictDi`, you would see something like this: - * - <example ng-app-included="true"> - <file name="index.html"> - <div ng-app="ngAppStrictDemo" ng-strict-di> - <div ng-controller="GoodController1"> - I can add: {{a}} + {{b}} = {{ a+b }} - - <p>This renders because the controller does not fail to - instantiate, by using explicit annotation style (see - script.js for details) - </p> - </div> - - <div ng-controller="GoodController2"> - Name: <input ng-model="name"><br /> - Hello, {{name}}! - - <p>This renders because the controller does not fail to - instantiate, by using explicit annotation style - (see script.js for details) - </p> - </div> - - <div ng-controller="BadController"> - I can add: {{a}} + {{b}} = {{ a+b }} - - <p>The controller could not be instantiated, due to relying - on automatic function annotations (which are disabled in - strict mode). As such, the content of this section is not - interpolated, and there should be an error in your web console. - </p> - </div> - </div> - </file> - <file name="script.js"> - angular.module('ngAppStrictDemo', []) - // BadController will fail to instantiate, due to relying on automatic function annotation, - // rather than an explicit annotation - .controller('BadController', function($scope) { - $scope.a = 1; - $scope.b = 2; - }) - // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, - // due to using explicit annotations using the array style and $inject property, respectively. - .controller('GoodController1', ['$scope', function($scope) { - $scope.a = 1; - $scope.b = 2; - }]) - .controller('GoodController2', GoodController2); - function GoodController2($scope) { - $scope.name = "World"; - } - GoodController2.$inject = ['$scope']; - </file> - <file name="style.css"> - div[ng-controller] { - margin-bottom: 1em; - -webkit-border-radius: 4px; - border-radius: 4px; - border: 1px solid; - padding: .5em; - } - div[ng-controller^=Good] { - border-color: #d6e9c6; - background-color: #dff0d8; - color: #3c763d; - } - div[ng-controller^=Bad] { - border-color: #ebccd1; - background-color: #f2dede; - color: #a94442; - margin-bottom: 0; - } - </file> - </example> - */ -function angularInit(element, bootstrap) { - var appElement, - module, - config = {}; - - // The element `element` has priority over any other element - forEach(ngAttrPrefixes, function(prefix) { - var name = prefix + 'app'; - - if (!appElement && element.hasAttribute && element.hasAttribute(name)) { - appElement = element; - module = element.getAttribute(name); - } - }); - forEach(ngAttrPrefixes, function(prefix) { - var name = prefix + 'app'; - var candidate; - - if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { - appElement = candidate; - module = candidate.getAttribute(name); - } - }); - if (appElement) { - config.strictDi = getNgAttribute(appElement, "strict-di") !== null; - bootstrap(appElement, module ? [module] : [], config); - } -} - -/** - * @ngdoc function - * @name angular.bootstrap - * @module ng - * @description - * Use this function to manually start up angular application. - * - * See: {@link guide/bootstrap Bootstrap} - * - * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually. - * They must use {@link ng.directive:ngApp ngApp}. - * - * Angular will detect if it has been loaded into the browser more than once and only allow the - * first loaded script to be bootstrapped and will report a warning to the browser console for - * each of the subsequent scripts. This prevents strange results in applications, where otherwise - * multiple instances of Angular try to work on the DOM. - * - * ```html - * <!doctype html> - * <html> - * <body> - * <div ng-controller="WelcomeController"> - * {{greeting}} - * </div> - * - * <script src="angular.js"></script> - * <script> - * var app = angular.module('demo', []) - * .controller('WelcomeController', function($scope) { - * $scope.greeting = 'Welcome!'; - * }); - * angular.bootstrap(document, ['demo']); - * </script> - * </body> - * </html> - * ``` - * - * @param {DOMElement} element DOM element which is the root of angular application. - * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. - * Each item in the array should be the name of a predefined module or a (DI annotated) - * function that will be invoked by the injector as a `config` block. - * See: {@link angular.module modules} - * @param {Object=} config an object for defining configuration options for the application. The - * following keys are supported: - * - * * `strictDi` - disable automatic function annotation for the application. This is meant to - * assist in finding bugs which break minified code. Defaults to `false`. - * - * @returns {auto.$injector} Returns the newly created injector for this app. - */ -function bootstrap(element, modules, config) { - if (!isObject(config)) config = {}; - var defaultConfig = { - strictDi: false - }; - config = extend(defaultConfig, config); - var doBootstrap = function() { - element = jqLite(element); - - if (element.injector()) { - var tag = (element[0] === document) ? 'document' : startingTag(element); - //Encode angle brackets to prevent input from being sanitized to empty string #8683 - throw ngMinErr( - 'btstrpd', - "App Already Bootstrapped with this Element '{0}'", - tag.replace(/</,'<').replace(/>/,'>')); - } - - modules = modules || []; - modules.unshift(['$provide', function($provide) { - $provide.value('$rootElement', element); - }]); - - if (config.debugInfoEnabled) { - // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. - modules.push(['$compileProvider', function($compileProvider) { - $compileProvider.debugInfoEnabled(true); - }]); - } - - modules.unshift('ng'); - var injector = createInjector(modules, config.strictDi); - injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', - function bootstrapApply(scope, element, compile, injector) { - scope.$apply(function() { - element.data('$injector', injector); - compile(element)(scope); - }); - }] - ); - return injector; - }; - - var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; - var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; - - if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { - config.debugInfoEnabled = true; - window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); - } - - if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { - return doBootstrap(); - } - - window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); - angular.resumeBootstrap = function(extraModules) { - forEach(extraModules, function(module) { - modules.push(module); - }); - return doBootstrap(); - }; - - if (isFunction(angular.resumeDeferredBootstrap)) { - angular.resumeDeferredBootstrap(); - } -} - -/** - * @ngdoc function - * @name angular.reloadWithDebugInfo - * @module ng - * @description - * Use this function to reload the current application with debug information turned on. - * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. - * - * See {@link ng.$compileProvider#debugInfoEnabled} for more. - */ -function reloadWithDebugInfo() { - window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; - window.location.reload(); -} - -/** - * @name angular.getTestability - * @module ng - * @description - * Get the testability service for the instance of Angular on the given - * element. - * @param {DOMElement} element DOM element which is the root of angular application. - */ -function getTestability(rootElement) { - var injector = angular.element(rootElement).injector(); - if (!injector) { - throw ngMinErr('test', - 'no injector found for element argument to getTestability'); - } - return injector.get('$$testability'); -} - -var SNAKE_CASE_REGEXP = /[A-Z]/g; -function snake_case(name, separator) { - separator = separator || '_'; - return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); -} - -var bindJQueryFired = false; -var skipDestroyOnNextJQueryCleanData; -function bindJQuery() { - var originalCleanData; - - if (bindJQueryFired) { - return; - } - - // bind to jQuery if present; - var jqName = jq(); - jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present) - !jqName ? undefined : // use jqLite - window[jqName]; // use jQuery specified by `ngJq` - - // Use jQuery if it exists with proper functionality, otherwise default to us. - // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. - // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older - // versions. It will not work for sure with jQuery <1.7, though. - if (jQuery && jQuery.fn.on) { - jqLite = jQuery; - extend(jQuery.fn, { - scope: JQLitePrototype.scope, - isolateScope: JQLitePrototype.isolateScope, - controller: JQLitePrototype.controller, - injector: JQLitePrototype.injector, - inheritedData: JQLitePrototype.inheritedData - }); - - // All nodes removed from the DOM via various jQuery APIs like .remove() - // are passed through jQuery.cleanData. Monkey-patch this method to fire - // the $destroy event on all removed nodes. - originalCleanData = jQuery.cleanData; - jQuery.cleanData = function(elems) { - var events; - if (!skipDestroyOnNextJQueryCleanData) { - for (var i = 0, elem; (elem = elems[i]) != null; i++) { - events = jQuery._data(elem, "events"); - if (events && events.$destroy) { - jQuery(elem).triggerHandler('$destroy'); - } - } - } else { - skipDestroyOnNextJQueryCleanData = false; - } - originalCleanData(elems); - }; - } else { - jqLite = JQLite; - } - - angular.element = jqLite; - - // Prevent double-proxying. - bindJQueryFired = true; -} - -/** - * throw error if the argument is falsy. - */ -function assertArg(arg, name, reason) { - if (!arg) { - throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); - } - return arg; -} - -function assertArgFn(arg, name, acceptArrayAnnotation) { - if (acceptArrayAnnotation && isArray(arg)) { - arg = arg[arg.length - 1]; - } - - assertArg(isFunction(arg), name, 'not a function, got ' + - (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); - return arg; -} - -/** - * throw error if the name given is hasOwnProperty - * @param {String} name the name to test - * @param {String} context the context in which the name is used, such as module or directive - */ -function assertNotHasOwnProperty(name, context) { - if (name === 'hasOwnProperty') { - throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); - } -} - -/** - * Return the value accessible from the object by path. Any undefined traversals are ignored - * @param {Object} obj starting object - * @param {String} path path to traverse - * @param {boolean} [bindFnToScope=true] - * @returns {Object} value as accessible by path - */ -//TODO(misko): this function needs to be removed -function getter(obj, path, bindFnToScope) { - if (!path) return obj; - var keys = path.split('.'); - var key; - var lastInstance = obj; - var len = keys.length; - - for (var i = 0; i < len; i++) { - key = keys[i]; - if (obj) { - obj = (lastInstance = obj)[key]; - } - } - if (!bindFnToScope && isFunction(obj)) { - return bind(lastInstance, obj); - } - return obj; -} - -/** - * Return the DOM siblings between the first and last node in the given array. - * @param {Array} array like object - * @returns {Array} the inputted object or a jqLite collection containing the nodes - */ -function getBlockNodes(nodes) { - // TODO(perf): update `nodes` instead of creating a new object? - var node = nodes[0]; - var endNode = nodes[nodes.length - 1]; - var blockNodes; - - for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { - if (blockNodes || nodes[i] !== node) { - if (!blockNodes) { - blockNodes = jqLite(slice.call(nodes, 0, i)); - } - blockNodes.push(node); - } - } - - return blockNodes || nodes; -} - - -/** - * Creates a new object without a prototype. This object is useful for lookup without having to - * guard against prototypically inherited properties via hasOwnProperty. - * - * Related micro-benchmarks: - * - http://jsperf.com/object-create2 - * - http://jsperf.com/proto-map-lookup/2 - * - http://jsperf.com/for-in-vs-object-keys2 - * - * @returns {Object} - */ -function createMap() { - return Object.create(null); -} - -var NODE_TYPE_ELEMENT = 1; -var NODE_TYPE_ATTRIBUTE = 2; -var NODE_TYPE_TEXT = 3; -var NODE_TYPE_COMMENT = 8; -var NODE_TYPE_DOCUMENT = 9; -var NODE_TYPE_DOCUMENT_FRAGMENT = 11; - -/** - * @ngdoc type - * @name angular.Module - * @module ng - * @description - * - * Interface for configuring angular {@link angular.module modules}. - */ - -function setupModuleLoader(window) { - - var $injectorMinErr = minErr('$injector'); - var ngMinErr = minErr('ng'); - - function ensure(obj, name, factory) { - return obj[name] || (obj[name] = factory()); - } - - var angular = ensure(window, 'angular', Object); - - // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap - angular.$$minErr = angular.$$minErr || minErr; - - return ensure(angular, 'module', function() { - /** @type {Object.<string, angular.Module>} */ - var modules = {}; - - /** - * @ngdoc function - * @name angular.module - * @module ng - * @description - * - * The `angular.module` is a global place for creating, registering and retrieving Angular - * modules. - * All modules (angular core or 3rd party) that should be available to an application must be - * registered using this mechanism. - * - * Passing one argument retrieves an existing {@link angular.Module}, - * whereas passing more than one argument creates a new {@link angular.Module} - * - * - * # Module - * - * A module is a collection of services, directives, controllers, filters, and configuration information. - * `angular.module` is used to configure the {@link auto.$injector $injector}. - * - * ```js - * // Create a new module - * var myModule = angular.module('myModule', []); - * - * // register a new service - * myModule.value('appName', 'MyCoolApp'); - * - * // configure existing services inside initialization blocks. - * myModule.config(['$locationProvider', function($locationProvider) { - * // Configure existing providers - * $locationProvider.hashPrefix('!'); - * }]); - * ``` - * - * Then you can create an injector and load your modules like this: - * - * ```js - * var injector = angular.injector(['ng', 'myModule']) - * ``` - * - * However it's more likely that you'll just use - * {@link ng.directive:ngApp ngApp} or - * {@link angular.bootstrap} to simplify this process for you. - * - * @param {!string} name The name of the module to create or retrieve. - * @param {!Array.<string>=} requires If specified then new module is being created. If - * unspecified then the module is being retrieved for further configuration. - * @param {Function=} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. - */ - return function module(name, requires, configFn) { - var assertNotHasOwnProperty = function(name, context) { - if (name === 'hasOwnProperty') { - throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); - } - }; - - assertNotHasOwnProperty(name, 'module'); - if (requires && modules.hasOwnProperty(name)) { - modules[name] = null; - } - return ensure(modules, name, function() { - if (!requires) { - throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + - "the module name or forgot to load it. If registering a module ensure that you " + - "specify the dependencies as the second argument.", name); - } - - /** @type {!Array.<Array.<*>>} */ - var invokeQueue = []; - - /** @type {!Array.<Function>} */ - var configBlocks = []; - - /** @type {!Array.<Function>} */ - var runBlocks = []; - - var config = invokeLater('$injector', 'invoke', 'push', configBlocks); - - /** @type {angular.Module} */ - var moduleInstance = { - // Private state - _invokeQueue: invokeQueue, - _configBlocks: configBlocks, - _runBlocks: runBlocks, - - /** - * @ngdoc property - * @name angular.Module#requires - * @module ng - * - * @description - * Holds the list of modules which the injector will load before the current module is - * loaded. - */ - requires: requires, - - /** - * @ngdoc property - * @name angular.Module#name - * @module ng - * - * @description - * Name of the module. - */ - name: name, - - - /** - * @ngdoc method - * @name angular.Module#provider - * @module ng - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the - * service. - * @description - * See {@link auto.$provide#provider $provide.provider()}. - */ - provider: invokeLaterAndSetModuleName('$provide', 'provider'), - - /** - * @ngdoc method - * @name angular.Module#factory - * @module ng - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. - * @description - * See {@link auto.$provide#factory $provide.factory()}. - */ - factory: invokeLaterAndSetModuleName('$provide', 'factory'), - - /** - * @ngdoc method - * @name angular.Module#service - * @module ng - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. - * @description - * See {@link auto.$provide#service $provide.service()}. - */ - service: invokeLaterAndSetModuleName('$provide', 'service'), - - /** - * @ngdoc method - * @name angular.Module#value - * @module ng - * @param {string} name service name - * @param {*} object Service instance object. - * @description - * See {@link auto.$provide#value $provide.value()}. - */ - value: invokeLater('$provide', 'value'), - - /** - * @ngdoc method - * @name angular.Module#constant - * @module ng - * @param {string} name constant name - * @param {*} object Constant value. - * @description - * Because the constant are fixed, they get applied before other provide methods. - * See {@link auto.$provide#constant $provide.constant()}. - */ - constant: invokeLater('$provide', 'constant', 'unshift'), - - /** - * @ngdoc method - * @name angular.Module#decorator - * @module ng - * @param {string} The name of the service to decorate. - * @param {Function} This function will be invoked when the service needs to be - * instantiated and should return the decorated service instance. - * @description - * See {@link auto.$provide#decorator $provide.decorator()}. - */ - decorator: invokeLaterAndSetModuleName('$provide', 'decorator'), - - /** - * @ngdoc method - * @name angular.Module#animation - * @module ng - * @param {string} name animation name - * @param {Function} animationFactory Factory function for creating new instance of an - * animation. - * @description - * - * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. - * - * - * Defines an animation hook that can be later used with - * {@link $animate $animate} service and directives that use this service. - * - * ```js - * module.animation('.animation-name', function($inject1, $inject2) { - * return { - * eventName : function(element, done) { - * //code to run the animation - * //once complete, then run done() - * return function cancellationFunction(element) { - * //code to cancel the animation - * } - * } - * } - * }) - * ``` - * - * See {@link ng.$animateProvider#register $animateProvider.register()} and - * {@link ngAnimate ngAnimate module} for more information. - */ - animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#filter - * @module ng - * @param {string} name Filter name - this must be a valid angular expression identifier - * @param {Function} filterFactory Factory function for creating new instance of filter. - * @description - * See {@link ng.$filterProvider#register $filterProvider.register()}. - * - * <div class="alert alert-warning"> - * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - * </div> - */ - filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#controller - * @module ng - * @param {string|Object} name Controller name, or an object map of controllers where the - * keys are the names and the values are the constructors. - * @param {Function} constructor Controller constructor function. - * @description - * See {@link ng.$controllerProvider#register $controllerProvider.register()}. - */ - controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#directive - * @module ng - * @param {string|Object} name Directive name, or an object map of directives where the - * keys are the names and the values are the factories. - * @param {Function} directiveFactory Factory function for creating new instance of - * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. - */ - directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), - - /** - * @ngdoc method - * @name angular.Module#config - * @module ng - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. - * @description - * Use this method to register work which needs to be performed on module loading. - * For more about how to configure services, see - * {@link providers#provider-recipe Provider Recipe}. - */ - config: config, - - /** - * @ngdoc method - * @name angular.Module#run - * @module ng - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. - * @description - * Use this method to register work which should be performed when the injector is done - * loading all modules. - */ - run: function(block) { - runBlocks.push(block); - return this; - } - }; - - if (configFn) { - config(configFn); - } - - return moduleInstance; - - /** - * @param {string} provider - * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} - */ - function invokeLater(provider, method, insertMethod, queue) { - if (!queue) queue = invokeQueue; - return function() { - queue[insertMethod || 'push']([provider, method, arguments]); - return moduleInstance; - }; - } - - /** - * @param {string} provider - * @param {string} method - * @returns {angular.Module} - */ - function invokeLaterAndSetModuleName(provider, method) { - return function(recipeName, factoryFunction) { - if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; - invokeQueue.push([provider, method, arguments]); - return moduleInstance; - }; - } - }); - }; - }); - -} - -/* global: toDebugString: true */ - -function serializeObject(obj) { - var seen = []; - - return JSON.stringify(obj, function(key, val) { - val = toJsonReplacer(key, val); - if (isObject(val)) { - - if (seen.indexOf(val) >= 0) return '...'; - - seen.push(val); - } - return val; - }); -} - -function toDebugString(obj) { - if (typeof obj === 'function') { - return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (isUndefined(obj)) { - return 'undefined'; - } else if (typeof obj !== 'string') { - return serializeObject(obj); - } - return obj; -} - -/* global angularModule: true, - version: true, - - $CompileProvider, - - htmlAnchorDirective, - inputDirective, - inputDirective, - formDirective, - scriptDirective, - selectDirective, - styleDirective, - optionDirective, - ngBindDirective, - ngBindHtmlDirective, - ngBindTemplateDirective, - ngClassDirective, - ngClassEvenDirective, - ngClassOddDirective, - ngCloakDirective, - ngControllerDirective, - ngFormDirective, - ngHideDirective, - ngIfDirective, - ngIncludeDirective, - ngIncludeFillContentDirective, - ngInitDirective, - ngNonBindableDirective, - ngPluralizeDirective, - ngRepeatDirective, - ngShowDirective, - ngStyleDirective, - ngSwitchDirective, - ngSwitchWhenDirective, - ngSwitchDefaultDirective, - ngOptionsDirective, - ngTranscludeDirective, - ngModelDirective, - ngListDirective, - ngChangeDirective, - patternDirective, - patternDirective, - requiredDirective, - requiredDirective, - minlengthDirective, - minlengthDirective, - maxlengthDirective, - maxlengthDirective, - ngValueDirective, - ngModelOptionsDirective, - ngAttributeAliasDirectives, - ngEventDirectives, - - $AnchorScrollProvider, - $AnimateProvider, - $CoreAnimateCssProvider, - $$CoreAnimateQueueProvider, - $$CoreAnimateRunnerProvider, - $BrowserProvider, - $CacheFactoryProvider, - $ControllerProvider, - $DocumentProvider, - $ExceptionHandlerProvider, - $FilterProvider, - $$ForceReflowProvider, - $InterpolateProvider, - $IntervalProvider, - $$HashMapProvider, - $HttpProvider, - $HttpParamSerializerProvider, - $HttpParamSerializerJQLikeProvider, - $HttpBackendProvider, - $xhrFactoryProvider, - $LocationProvider, - $LogProvider, - $ParseProvider, - $RootScopeProvider, - $QProvider, - $$QProvider, - $$SanitizeUriProvider, - $SceProvider, - $SceDelegateProvider, - $SnifferProvider, - $TemplateCacheProvider, - $TemplateRequestProvider, - $$TestabilityProvider, - $TimeoutProvider, - $$RAFProvider, - $WindowProvider, - $$jqLiteProvider, - $$CookieReaderProvider -*/ - - -/** - * @ngdoc object - * @name angular.version - * @module ng - * @description - * An object that contains information about the current AngularJS version. - * - * This object has the following properties: - * - * - `full` – `{string}` – Full version string, such as "0.9.18". - * - `major` – `{number}` – Major version number, such as "0". - * - `minor` – `{number}` – Minor version number, such as "9". - * - `dot` – `{number}` – Dot version number, such as "18". - * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". - */ -var version = { - full: '1.4.7', // all of these placeholder strings will be replaced by grunt's - major: 1, // package task - minor: 4, - dot: 7, - codeName: 'dark-luminescence' -}; - - -function publishExternalAPI(angular) { - extend(angular, { - 'bootstrap': bootstrap, - 'copy': copy, - 'extend': extend, - 'merge': merge, - 'equals': equals, - 'element': jqLite, - 'forEach': forEach, - 'injector': createInjector, - 'noop': noop, - 'bind': bind, - 'toJson': toJson, - 'fromJson': fromJson, - 'identity': identity, - 'isUndefined': isUndefined, - 'isDefined': isDefined, - 'isString': isString, - 'isFunction': isFunction, - 'isObject': isObject, - 'isNumber': isNumber, - 'isElement': isElement, - 'isArray': isArray, - 'version': version, - 'isDate': isDate, - 'lowercase': lowercase, - 'uppercase': uppercase, - 'callbacks': {counter: 0}, - 'getTestability': getTestability, - '$$minErr': minErr, - '$$csp': csp, - 'reloadWithDebugInfo': reloadWithDebugInfo - }); - - angularModule = setupModuleLoader(window); - - angularModule('ng', ['ngLocale'], ['$provide', - function ngModule($provide) { - // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. - $provide.provider({ - $$sanitizeUri: $$SanitizeUriProvider - }); - $provide.provider('$compile', $CompileProvider). - directive({ - a: htmlAnchorDirective, - input: inputDirective, - textarea: inputDirective, - form: formDirective, - script: scriptDirective, - select: selectDirective, - style: styleDirective, - option: optionDirective, - ngBind: ngBindDirective, - ngBindHtml: ngBindHtmlDirective, - ngBindTemplate: ngBindTemplateDirective, - ngClass: ngClassDirective, - ngClassEven: ngClassEvenDirective, - ngClassOdd: ngClassOddDirective, - ngCloak: ngCloakDirective, - ngController: ngControllerDirective, - ngForm: ngFormDirective, - ngHide: ngHideDirective, - ngIf: ngIfDirective, - ngInclude: ngIncludeDirective, - ngInit: ngInitDirective, - ngNonBindable: ngNonBindableDirective, - ngPluralize: ngPluralizeDirective, - ngRepeat: ngRepeatDirective, - ngShow: ngShowDirective, - ngStyle: ngStyleDirective, - ngSwitch: ngSwitchDirective, - ngSwitchWhen: ngSwitchWhenDirective, - ngSwitchDefault: ngSwitchDefaultDirective, - ngOptions: ngOptionsDirective, - ngTransclude: ngTranscludeDirective, - ngModel: ngModelDirective, - ngList: ngListDirective, - ngChange: ngChangeDirective, - pattern: patternDirective, - ngPattern: patternDirective, - required: requiredDirective, - ngRequired: requiredDirective, - minlength: minlengthDirective, - ngMinlength: minlengthDirective, - maxlength: maxlengthDirective, - ngMaxlength: maxlengthDirective, - ngValue: ngValueDirective, - ngModelOptions: ngModelOptionsDirective - }). - directive({ - ngInclude: ngIncludeFillContentDirective - }). - directive(ngAttributeAliasDirectives). - directive(ngEventDirectives); - $provide.provider({ - $anchorScroll: $AnchorScrollProvider, - $animate: $AnimateProvider, - $animateCss: $CoreAnimateCssProvider, - $$animateQueue: $$CoreAnimateQueueProvider, - $$AnimateRunner: $$CoreAnimateRunnerProvider, - $browser: $BrowserProvider, - $cacheFactory: $CacheFactoryProvider, - $controller: $ControllerProvider, - $document: $DocumentProvider, - $exceptionHandler: $ExceptionHandlerProvider, - $filter: $FilterProvider, - $$forceReflow: $$ForceReflowProvider, - $interpolate: $InterpolateProvider, - $interval: $IntervalProvider, - $http: $HttpProvider, - $httpParamSerializer: $HttpParamSerializerProvider, - $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, - $httpBackend: $HttpBackendProvider, - $xhrFactory: $xhrFactoryProvider, - $location: $LocationProvider, - $log: $LogProvider, - $parse: $ParseProvider, - $rootScope: $RootScopeProvider, - $q: $QProvider, - $$q: $$QProvider, - $sce: $SceProvider, - $sceDelegate: $SceDelegateProvider, - $sniffer: $SnifferProvider, - $templateCache: $TemplateCacheProvider, - $templateRequest: $TemplateRequestProvider, - $$testability: $$TestabilityProvider, - $timeout: $TimeoutProvider, - $window: $WindowProvider, - $$rAF: $$RAFProvider, - $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, - $$cookieReader: $$CookieReaderProvider - }); - } - ]); -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* global JQLitePrototype: true, - addEventListenerFn: true, - removeEventListenerFn: true, - BOOLEAN_ATTR: true, - ALIASED_ATTR: true, -*/ - -////////////////////////////////// -//JQLite -////////////////////////////////// - -/** - * @ngdoc function - * @name angular.element - * @module ng - * @kind function - * - * @description - * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. - * - * If jQuery is available, `angular.element` is an alias for the - * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` - * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." - * - * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows - * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most - * commonly needed functionality with the goal of having a very small footprint.</div> - * - * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. - * - * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or - * jqLite; they are never raw DOM references.</div> - * - * ## Angular's jqLite - * jqLite provides only the following jQuery methods: - * - * - [`addClass()`](http://api.jquery.com/addClass/) - * - [`after()`](http://api.jquery.com/after/) - * - [`append()`](http://api.jquery.com/append/) - * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters - * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData - * - [`children()`](http://api.jquery.com/children/) - Does not support selectors - * - [`clone()`](http://api.jquery.com/clone/) - * - [`contents()`](http://api.jquery.com/contents/) - * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'. - * - [`data()`](http://api.jquery.com/data/) - * - [`detach()`](http://api.jquery.com/detach/) - * - [`empty()`](http://api.jquery.com/empty/) - * - [`eq()`](http://api.jquery.com/eq/) - * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name - * - [`hasClass()`](http://api.jquery.com/hasClass/) - * - [`html()`](http://api.jquery.com/html/) - * - [`next()`](http://api.jquery.com/next/) - Does not support selectors - * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData - * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter - * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors - * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors - * - [`prepend()`](http://api.jquery.com/prepend/) - * - [`prop()`](http://api.jquery.com/prop/) - * - [`ready()`](http://api.jquery.com/ready/) - * - [`remove()`](http://api.jquery.com/remove/) - * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - * - [`removeClass()`](http://api.jquery.com/removeClass/) - * - [`removeData()`](http://api.jquery.com/removeData/) - * - [`replaceWith()`](http://api.jquery.com/replaceWith/) - * - [`text()`](http://api.jquery.com/text/) - * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. - * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter - * - [`val()`](http://api.jquery.com/val/) - * - [`wrap()`](http://api.jquery.com/wrap/) - * - * ## jQuery/jqLite Extras - * Angular also provides the following additional methods and events to both jQuery and jqLite: - * - * ### Events - * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event - * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM - * element before it is removed. - * - * ### Methods - * - `controller(name)` - retrieves the controller of the current element or its parent. By default - * retrieves controller associated with the `ngController` directive. If `name` is provided as - * camelCase directive name, then the controller for this directive will be retrieved (e.g. - * `'ngModel'`). - * - `injector()` - retrieves the injector of the current element or its parent. - * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current - * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to - * be enabled. - * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the - * current element. This getter should be used only on elements that contain a directive which starts a new isolate - * scope. Calling `scope()` on this element always returns the original non-isolate scope. - * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. - * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top - * parent element is reached. - * - * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. - * @returns {Object} jQuery object. - */ - -JQLite.expando = 'ng339'; - -var jqCache = JQLite.cache = {}, - jqId = 1, - addEventListenerFn = function(element, type, fn) { - element.addEventListener(type, fn, false); - }, - removeEventListenerFn = function(element, type, fn) { - element.removeEventListener(type, fn, false); - }; - -/* - * !!! This is an undocumented "private" function !!! - */ -JQLite._data = function(node) { - //jQuery always returns an object on cache miss - return this.cache[node[this.expando]] || {}; -}; - -function jqNextId() { return ++jqId; } - - -var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; -var MOZ_HACK_REGEXP = /^moz([A-Z])/; -var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; -var jqLiteMinErr = minErr('jqLite'); - -/** - * Converts snake_case to camelCase. - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function camelCase(name) { - return name. - replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }). - replace(MOZ_HACK_REGEXP, 'Moz$1'); -} - -var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/; -var HTML_REGEXP = /<|&#?\w+;/; -var TAG_NAME_REGEXP = /<([\w:-]+)/; -var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi; - -var wrapMap = { - 'option': [1, '<select multiple="multiple">', '</select>'], - - 'thead': [1, '<table>', '</table>'], - 'col': [2, '<table><colgroup>', '</colgroup></table>'], - 'tr': [2, '<table><tbody>', '</tbody></table>'], - 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], - '_default': [0, "", ""] -}; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - - -function jqLiteIsTextNode(html) { - return !HTML_REGEXP.test(html); -} - -function jqLiteAcceptsData(node) { - // The window object can accept data but has no nodeType - // Otherwise we are only interested in elements (1) and documents (9) - var nodeType = node.nodeType; - return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; -} - -function jqLiteHasData(node) { - for (var key in jqCache[node.ng339]) { - return true; - } - return false; -} - -function jqLiteBuildFragment(html, context) { - var tmp, tag, wrap, - fragment = context.createDocumentFragment(), - nodes = [], i; - - if (jqLiteIsTextNode(html)) { - // Convert non-html into a text node - nodes.push(context.createTextNode(html)); - } else { - // Convert html into DOM nodes - tmp = tmp || fragment.appendChild(context.createElement("div")); - tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); - wrap = wrapMap[tag] || wrapMap._default; - tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2]; - - // Descend through wrappers to the right content - i = wrap[0]; - while (i--) { - tmp = tmp.lastChild; - } - - nodes = concat(nodes, tmp.childNodes); - - tmp = fragment.firstChild; - tmp.textContent = ""; - } - - // Remove wrapper from fragment - fragment.textContent = ""; - fragment.innerHTML = ""; // Clear inner HTML - forEach(nodes, function(node) { - fragment.appendChild(node); - }); - - return fragment; -} - -function jqLiteParseHTML(html, context) { - context = context || document; - var parsed; - - if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { - return [context.createElement(parsed[1])]; - } - - if ((parsed = jqLiteBuildFragment(html, context))) { - return parsed.childNodes; - } - - return []; -} - -///////////////////////////////////////////// -function JQLite(element) { - if (element instanceof JQLite) { - return element; - } - - var argIsString; - - if (isString(element)) { - element = trim(element); - argIsString = true; - } - if (!(this instanceof JQLite)) { - if (argIsString && element.charAt(0) != '<') { - throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); - } - return new JQLite(element); - } - - if (argIsString) { - jqLiteAddNodes(this, jqLiteParseHTML(element)); - } else { - jqLiteAddNodes(this, element); - } -} - -function jqLiteClone(element) { - return element.cloneNode(true); -} - -function jqLiteDealoc(element, onlyDescendants) { - if (!onlyDescendants) jqLiteRemoveData(element); - - if (element.querySelectorAll) { - var descendants = element.querySelectorAll('*'); - for (var i = 0, l = descendants.length; i < l; i++) { - jqLiteRemoveData(descendants[i]); - } - } -} - -function jqLiteOff(element, type, fn, unsupported) { - if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); - - var expandoStore = jqLiteExpandoStore(element); - var events = expandoStore && expandoStore.events; - var handle = expandoStore && expandoStore.handle; - - if (!handle) return; //no listeners registered - - if (!type) { - for (type in events) { - if (type !== '$destroy') { - removeEventListenerFn(element, type, handle); - } - delete events[type]; - } - } else { - forEach(type.split(' '), function(type) { - if (isDefined(fn)) { - var listenerFns = events[type]; - arrayRemove(listenerFns || [], fn); - if (listenerFns && listenerFns.length > 0) { - return; - } - } - - removeEventListenerFn(element, type, handle); - delete events[type]; - }); - } -} - -function jqLiteRemoveData(element, name) { - var expandoId = element.ng339; - var expandoStore = expandoId && jqCache[expandoId]; - - if (expandoStore) { - if (name) { - delete expandoStore.data[name]; - return; - } - - if (expandoStore.handle) { - if (expandoStore.events.$destroy) { - expandoStore.handle({}, '$destroy'); - } - jqLiteOff(element); - } - delete jqCache[expandoId]; - element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it - } -} - - -function jqLiteExpandoStore(element, createIfNecessary) { - var expandoId = element.ng339, - expandoStore = expandoId && jqCache[expandoId]; - - if (createIfNecessary && !expandoStore) { - element.ng339 = expandoId = jqNextId(); - expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; - } - - return expandoStore; -} - - -function jqLiteData(element, key, value) { - if (jqLiteAcceptsData(element)) { - - var isSimpleSetter = isDefined(value); - var isSimpleGetter = !isSimpleSetter && key && !isObject(key); - var massGetter = !key; - var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); - var data = expandoStore && expandoStore.data; - - if (isSimpleSetter) { // data('key', value) - data[key] = value; - } else { - if (massGetter) { // data() - return data; - } else { - if (isSimpleGetter) { // data('key') - // don't force creation of expandoStore if it doesn't exist yet - return data && data[key]; - } else { // mass-setter: data({key1: val1, key2: val2}) - extend(data, key); - } - } - } - } -} - -function jqLiteHasClass(element, selector) { - if (!element.getAttribute) return false; - return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). - indexOf(" " + selector + " ") > -1); -} - -function jqLiteRemoveClass(element, cssClasses) { - if (cssClasses && element.setAttribute) { - forEach(cssClasses.split(' '), function(cssClass) { - element.setAttribute('class', trim( - (" " + (element.getAttribute('class') || '') + " ") - .replace(/[\n\t]/g, " ") - .replace(" " + trim(cssClass) + " ", " ")) - ); - }); - } -} - -function jqLiteAddClass(element, cssClasses) { - if (cssClasses && element.setAttribute) { - var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') - .replace(/[\n\t]/g, " "); - - forEach(cssClasses.split(' '), function(cssClass) { - cssClass = trim(cssClass); - if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { - existingClasses += cssClass + ' '; - } - }); - - element.setAttribute('class', trim(existingClasses)); - } -} - - -function jqLiteAddNodes(root, elements) { - // THIS CODE IS VERY HOT. Don't make changes without benchmarking. - - if (elements) { - - // if a Node (the most common case) - if (elements.nodeType) { - root[root.length++] = elements; - } else { - var length = elements.length; - - // if an Array or NodeList and not a Window - if (typeof length === 'number' && elements.window !== elements) { - if (length) { - for (var i = 0; i < length; i++) { - root[root.length++] = elements[i]; - } - } - } else { - root[root.length++] = elements; - } - } - } -} - - -function jqLiteController(element, name) { - return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); -} - -function jqLiteInheritedData(element, name, value) { - // if element is the document object work with the html element instead - // this makes $(document).scope() possible - if (element.nodeType == NODE_TYPE_DOCUMENT) { - element = element.documentElement; - } - var names = isArray(name) ? name : [name]; - - while (element) { - for (var i = 0, ii = names.length; i < ii; i++) { - if (isDefined(value = jqLite.data(element, names[i]))) return value; - } - - // If dealing with a document fragment node with a host element, and no parent, use the host - // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM - // to lookup parent controllers. - element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); - } -} - -function jqLiteEmpty(element) { - jqLiteDealoc(element, true); - while (element.firstChild) { - element.removeChild(element.firstChild); - } -} - -function jqLiteRemove(element, keepData) { - if (!keepData) jqLiteDealoc(element); - var parent = element.parentNode; - if (parent) parent.removeChild(element); -} - - -function jqLiteDocumentLoaded(action, win) { - win = win || window; - if (win.document.readyState === 'complete') { - // Force the action to be run async for consistent behaviour - // from the action's point of view - // i.e. it will definitely not be in a $apply - win.setTimeout(action); - } else { - // No need to unbind this handler as load is only ever called once - jqLite(win).on('load', action); - } -} - -////////////////////////////////////////// -// Functions which are declared directly. -////////////////////////////////////////// -var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - // check if document is already loaded - if (document.readyState === 'complete') { - setTimeout(trigger); - } else { - this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - // jshint -W064 - JQLite(window).on('load', trigger); // fallback to window.onload for others - // jshint +W064 - } - }, - toString: function() { - var value = []; - forEach(this, function(e) { value.push('' + e);}); - return '[' + value.join(', ') + ']'; - }, - - eq: function(index) { - return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); - }, - - length: 0, - push: push, - sort: [].sort, - splice: [].splice -}; - -////////////////////////////////////////// -// Functions iterating getter/setters. -// these functions return self on setter and -// value on get. -////////////////////////////////////////// -var BOOLEAN_ATTR = {}; -forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { - BOOLEAN_ATTR[lowercase(value)] = value; -}); -var BOOLEAN_ELEMENTS = {}; -forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { - BOOLEAN_ELEMENTS[value] = true; -}); -var ALIASED_ATTR = { - 'ngMinlength': 'minlength', - 'ngMaxlength': 'maxlength', - 'ngMin': 'min', - 'ngMax': 'max', - 'ngPattern': 'pattern' -}; - -function getBooleanAttrName(element, name) { - // check dom last since we will most likely fail on name - var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; - - // booleanAttr is here twice to minimize DOM access - return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; -} - -function getAliasedAttrName(name) { - return ALIASED_ATTR[name]; -} - -forEach({ - data: jqLiteData, - removeData: jqLiteRemoveData, - hasData: jqLiteHasData -}, function(fn, name) { - JQLite[name] = fn; -}); - -forEach({ - data: jqLiteData, - inheritedData: jqLiteInheritedData, - - scope: function(element) { - // Can't use jqLiteData here directly so we stay compatible with jQuery! - return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); - }, - - isolateScope: function(element) { - // Can't use jqLiteData here directly so we stay compatible with jQuery! - return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); - }, - - controller: jqLiteController, - - injector: function(element) { - return jqLiteInheritedData(element, '$injector'); - }, - - removeAttr: function(element, name) { - element.removeAttribute(name); - }, - - hasClass: jqLiteHasClass, - - css: function(element, name, value) { - name = camelCase(name); - - if (isDefined(value)) { - element.style[name] = value; - } else { - return element.style[name]; - } - }, - - attr: function(element, name, value) { - var nodeType = element.nodeType; - if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) { - return; - } - var lowercasedName = lowercase(name); - if (BOOLEAN_ATTR[lowercasedName]) { - if (isDefined(value)) { - if (!!value) { - element[name] = true; - element.setAttribute(name, lowercasedName); - } else { - element[name] = false; - element.removeAttribute(lowercasedName); - } - } else { - return (element[name] || - (element.attributes.getNamedItem(name) || noop).specified) - ? lowercasedName - : undefined; - } - } else if (isDefined(value)) { - element.setAttribute(name, value); - } else if (element.getAttribute) { - // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code - // some elements (e.g. Document) don't have get attribute, so return undefined - var ret = element.getAttribute(name, 2); - // normalize non-existing attributes to undefined (as jQuery) - return ret === null ? undefined : ret; - } - }, - - prop: function(element, name, value) { - if (isDefined(value)) { - element[name] = value; - } else { - return element[name]; - } - }, - - text: (function() { - getText.$dv = ''; - return getText; - - function getText(element, value) { - if (isUndefined(value)) { - var nodeType = element.nodeType; - return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; - } - element.textContent = value; - } - })(), - - val: function(element, value) { - if (isUndefined(value)) { - if (element.multiple && nodeName_(element) === 'select') { - var result = []; - forEach(element.options, function(option) { - if (option.selected) { - result.push(option.value || option.text); - } - }); - return result.length === 0 ? null : result; - } - return element.value; - } - element.value = value; - }, - - html: function(element, value) { - if (isUndefined(value)) { - return element.innerHTML; - } - jqLiteDealoc(element, true); - element.innerHTML = value; - }, - - empty: jqLiteEmpty -}, function(fn, name) { - /** - * Properties: writes return selection, reads return first value - */ - JQLite.prototype[name] = function(arg1, arg2) { - var i, key; - var nodeCount = this.length; - - // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it - // in a way that survives minification. - // jqLiteEmpty takes no arguments but is a setter. - if (fn !== jqLiteEmpty && - (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) { - if (isObject(arg1)) { - - // we are a write, but the object properties are the key/values - for (i = 0; i < nodeCount; i++) { - if (fn === jqLiteData) { - // data() takes the whole object in jQuery - fn(this[i], arg1); - } else { - for (key in arg1) { - fn(this[i], key, arg1[key]); - } - } - } - // return self for chaining - return this; - } else { - // we are a read, so read the first child. - // TODO: do we still need this? - var value = fn.$dv; - // Only if we have $dv do we iterate over all, otherwise it is just the first element. - var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount; - for (var j = 0; j < jj; j++) { - var nodeValue = fn(this[j], arg1, arg2); - value = value ? value + nodeValue : nodeValue; - } - return value; - } - } else { - // we are a write, so apply to all children - for (i = 0; i < nodeCount; i++) { - fn(this[i], arg1, arg2); - } - // return self for chaining - return this; - } - }; -}); - -function createEventHandler(element, events) { - var eventHandler = function(event, type) { - // jQuery specific api - event.isDefaultPrevented = function() { - return event.defaultPrevented; - }; - - var eventFns = events[type || event.type]; - var eventFnsLength = eventFns ? eventFns.length : 0; - - if (!eventFnsLength) return; - - if (isUndefined(event.immediatePropagationStopped)) { - var originalStopImmediatePropagation = event.stopImmediatePropagation; - event.stopImmediatePropagation = function() { - event.immediatePropagationStopped = true; - - if (event.stopPropagation) { - event.stopPropagation(); - } - - if (originalStopImmediatePropagation) { - originalStopImmediatePropagation.call(event); - } - }; - } - - event.isImmediatePropagationStopped = function() { - return event.immediatePropagationStopped === true; - }; - - // Copy event handlers in case event handlers array is modified during execution. - if ((eventFnsLength > 1)) { - eventFns = shallowCopy(eventFns); - } - - for (var i = 0; i < eventFnsLength; i++) { - if (!event.isImmediatePropagationStopped()) { - eventFns[i].call(element, event); - } - } - }; - - // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all - // events on `element` - eventHandler.elem = element; - return eventHandler; -} - -////////////////////////////////////////// -// Functions iterating traversal. -// These functions chain results into a single -// selector. -////////////////////////////////////////// -forEach({ - removeData: jqLiteRemoveData, - - on: function jqLiteOn(element, type, fn, unsupported) { - if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); - - // Do not add event handlers to non-elements because they will not be cleaned up. - if (!jqLiteAcceptsData(element)) { - return; - } - - var expandoStore = jqLiteExpandoStore(element, true); - var events = expandoStore.events; - var handle = expandoStore.handle; - - if (!handle) { - handle = expandoStore.handle = createEventHandler(element, events); - } - - // http://jsperf.com/string-indexof-vs-split - var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; - var i = types.length; - - while (i--) { - type = types[i]; - var eventFns = events[type]; - - if (!eventFns) { - events[type] = []; - - if (type === 'mouseenter' || type === 'mouseleave') { - // Refer to jQuery's implementation of mouseenter & mouseleave - // Read about mouseenter and mouseleave: - // http://www.quirksmode.org/js/events_mouse.html#link8 - - jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { - var target = this, related = event.relatedTarget; - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if (!related || (related !== target && !target.contains(related))) { - handle(event, type); - } - }); - - } else { - if (type !== '$destroy') { - addEventListenerFn(element, type, handle); - } - } - eventFns = events[type]; - } - eventFns.push(fn); - } - }, - - off: jqLiteOff, - - one: function(element, type, fn) { - element = jqLite(element); - - //add the listener twice so that when it is called - //you can remove the original function and still be - //able to call element.off(ev, fn) normally - element.on(type, function onFn() { - element.off(type, fn); - element.off(type, onFn); - }); - element.on(type, fn); - }, - - replaceWith: function(element, replaceNode) { - var index, parent = element.parentNode; - jqLiteDealoc(element); - forEach(new JQLite(replaceNode), function(node) { - if (index) { - parent.insertBefore(node, index.nextSibling); - } else { - parent.replaceChild(node, element); - } - index = node; - }); - }, - - children: function(element) { - var children = []; - forEach(element.childNodes, function(element) { - if (element.nodeType === NODE_TYPE_ELEMENT) { - children.push(element); - } - }); - return children; - }, - - contents: function(element) { - return element.contentDocument || element.childNodes || []; - }, - - append: function(element, node) { - var nodeType = element.nodeType; - if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; - - node = new JQLite(node); - - for (var i = 0, ii = node.length; i < ii; i++) { - var child = node[i]; - element.appendChild(child); - } - }, - - prepend: function(element, node) { - if (element.nodeType === NODE_TYPE_ELEMENT) { - var index = element.firstChild; - forEach(new JQLite(node), function(child) { - element.insertBefore(child, index); - }); - } - }, - - wrap: function(element, wrapNode) { - wrapNode = jqLite(wrapNode).eq(0).clone()[0]; - var parent = element.parentNode; - if (parent) { - parent.replaceChild(wrapNode, element); - } - wrapNode.appendChild(element); - }, - - remove: jqLiteRemove, - - detach: function(element) { - jqLiteRemove(element, true); - }, - - after: function(element, newElement) { - var index = element, parent = element.parentNode; - newElement = new JQLite(newElement); - - for (var i = 0, ii = newElement.length; i < ii; i++) { - var node = newElement[i]; - parent.insertBefore(node, index.nextSibling); - index = node; - } - }, - - addClass: jqLiteAddClass, - removeClass: jqLiteRemoveClass, - - toggleClass: function(element, selector, condition) { - if (selector) { - forEach(selector.split(' '), function(className) { - var classCondition = condition; - if (isUndefined(classCondition)) { - classCondition = !jqLiteHasClass(element, className); - } - (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); - }); - } - }, - - parent: function(element) { - var parent = element.parentNode; - return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; - }, - - next: function(element) { - return element.nextElementSibling; - }, - - find: function(element, selector) { - if (element.getElementsByTagName) { - return element.getElementsByTagName(selector); - } else { - return []; - } - }, - - clone: jqLiteClone, - - triggerHandler: function(element, event, extraParameters) { - - var dummyEvent, eventFnsCopy, handlerArgs; - var eventName = event.type || event; - var expandoStore = jqLiteExpandoStore(element); - var events = expandoStore && expandoStore.events; - var eventFns = events && events[eventName]; - - if (eventFns) { - // Create a dummy event to pass to the handlers - dummyEvent = { - preventDefault: function() { this.defaultPrevented = true; }, - isDefaultPrevented: function() { return this.defaultPrevented === true; }, - stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, - isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, - stopPropagation: noop, - type: eventName, - target: element - }; - - // If a custom event was provided then extend our dummy event with it - if (event.type) { - dummyEvent = extend(dummyEvent, event); - } - - // Copy event handlers in case event handlers array is modified during execution. - eventFnsCopy = shallowCopy(eventFns); - handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; - - forEach(eventFnsCopy, function(fn) { - if (!dummyEvent.isImmediatePropagationStopped()) { - fn.apply(element, handlerArgs); - } - }); - } - } -}, function(fn, name) { - /** - * chaining functions - */ - JQLite.prototype[name] = function(arg1, arg2, arg3) { - var value; - - for (var i = 0, ii = this.length; i < ii; i++) { - if (isUndefined(value)) { - value = fn(this[i], arg1, arg2, arg3); - if (isDefined(value)) { - // any function which returns a value needs to be wrapped - value = jqLite(value); - } - } else { - jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); - } - } - return isDefined(value) ? value : this; - }; - - // bind legacy bind/unbind to on/off - JQLite.prototype.bind = JQLite.prototype.on; - JQLite.prototype.unbind = JQLite.prototype.off; -}); - - -// Provider for private $$jqLite service -function $$jqLiteProvider() { - this.$get = function $$jqLite() { - return extend(JQLite, { - hasClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteHasClass(node, classes); - }, - addClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteAddClass(node, classes); - }, - removeClass: function(node, classes) { - if (node.attr) node = node[0]; - return jqLiteRemoveClass(node, classes); - } - }); - }; -} - -/** - * Computes a hash of an 'obj'. - * Hash of a: - * string is string - * number is number as string - * object is either result of calling $$hashKey function on the object or uniquely generated id, - * that is also assigned to the $$hashKey property of the object. - * - * @param obj - * @returns {string} hash string such that the same input will have the same hash string. - * The resulting string key is in 'type:hashKey' format. - */ -function hashKey(obj, nextUidFn) { - var key = obj && obj.$$hashKey; - - if (key) { - if (typeof key === 'function') { - key = obj.$$hashKey(); - } - return key; - } - - var objType = typeof obj; - if (objType == 'function' || (objType == 'object' && obj !== null)) { - key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); - } else { - key = objType + ':' + obj; - } - - return key; -} - -/** - * HashMap which can use objects as keys - */ -function HashMap(array, isolatedUid) { - if (isolatedUid) { - var uid = 0; - this.nextUid = function() { - return ++uid; - }; - } - forEach(array, this.put, this); -} -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key, this.nextUid)] = value; - }, - - /** - * @param key - * @returns {Object} the value for the key - */ - get: function(key) { - return this[hashKey(key, this.nextUid)]; - }, - - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key, this.nextUid)]; - delete this[key]; - return value; - } -}; - -var $$HashMapProvider = [function() { - this.$get = [function() { - return HashMap; - }]; -}]; - -/** - * @ngdoc function - * @module ng - * @name angular.injector - * @kind function - * - * @description - * Creates an injector object that can be used for retrieving services as well as for - * dependency injection (see {@link guide/di dependency injection}). - * - * @param {Array.<string|Function>} modules A list of module functions or their aliases. See - * {@link angular.module}. The `ng` module must be explicitly added. - * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which - * disallows argument name annotation inference. - * @returns {injector} Injector object. See {@link auto.$injector $injector}. - * - * @example - * Typical usage - * ```js - * // create an injector - * var $injector = angular.injector(['ng']); - * - * // use the injector to kick off your application - * // use the type inference to auto inject arguments, or use implicit injection - * $injector.invoke(function($rootScope, $compile, $document) { - * $compile($document)($rootScope); - * $rootScope.$digest(); - * }); - * ``` - * - * Sometimes you want to get access to the injector of a currently running Angular app - * from outside Angular. Perhaps, you want to inject and compile some markup after the - * application has been bootstrapped. You can do this using the extra `injector()` added - * to JQuery/jqLite elements. See {@link angular.element}. - * - * *This is fairly rare but could be the case if a third party library is injecting the - * markup.* - * - * In the following example a new block of HTML containing a `ng-controller` - * directive is added to the end of the document body by JQuery. We then compile and link - * it into the current AngularJS scope. - * - * ```js - * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); - * $(document.body).append($div); - * - * angular.element(document).injector().invoke(function($compile) { - * var scope = angular.element($div).scope(); - * $compile($div)(scope); - * }); - * ``` - */ - - -/** - * @ngdoc module - * @name auto - * @description - * - * Implicit module which gets automatically added to each {@link auto.$injector $injector}. - */ - -var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; -var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; -var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -var $injectorMinErr = minErr('$injector'); - -function anonFn(fn) { - // For anonymous functions, showing at the very least the function signature can help in - // debugging. - var fnText = fn.toString().replace(STRIP_COMMENTS, ''), - args = fnText.match(FN_ARGS); - if (args) { - return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; - } - return 'fn'; -} - -function annotate(fn, strictDi, name) { - var $inject, - fnText, - argDecl, - last; - - if (typeof fn === 'function') { - if (!($inject = fn.$inject)) { - $inject = []; - if (fn.length) { - if (strictDi) { - if (!isString(name) || !name) { - name = fn.name || anonFn(fn); - } - throw $injectorMinErr('strictdi', - '{0} is not using explicit annotation and cannot be invoked in strict mode', name); - } - fnText = fn.toString().replace(STRIP_COMMENTS, ''); - argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { - arg.replace(FN_ARG, function(all, underscore, name) { - $inject.push(name); - }); - }); - } - fn.$inject = $inject; - } - } else if (isArray(fn)) { - last = fn.length - 1; - assertArgFn(fn[last], 'fn'); - $inject = fn.slice(0, last); - } else { - assertArgFn(fn, 'fn', true); - } - return $inject; -} - -/////////////////////////////////////// - -/** - * @ngdoc service - * @name $injector - * - * @description - * - * `$injector` is used to retrieve object instances as defined by - * {@link auto.$provide provider}, instantiate types, invoke methods, - * and load modules. - * - * The following always holds true: - * - * ```js - * var $injector = angular.injector(); - * expect($injector.get('$injector')).toBe($injector); - * expect($injector.invoke(function($injector) { - * return $injector; - * })).toBe($injector); - * ``` - * - * # Injection Function Annotation - * - * JavaScript does not have annotations, and annotations are needed for dependency injection. The - * following are all valid ways of annotating function with injection arguments and are equivalent. - * - * ```js - * // inferred (only works if code not minified/obfuscated) - * $injector.invoke(function(serviceA){}); - * - * // annotated - * function explicit(serviceA) {}; - * explicit.$inject = ['serviceA']; - * $injector.invoke(explicit); - * - * // inline - * $injector.invoke(['serviceA', function(serviceA){}]); - * ``` - * - * ## Inference - * - * In JavaScript calling `toString()` on a function returns the function definition. The definition - * can then be parsed and the function arguments can be extracted. This method of discovering - * annotations is disallowed when the injector is in strict mode. - * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the - * argument names. - * - * ## `$inject` Annotation - * By adding an `$inject` property onto a function the injection parameters can be specified. - * - * ## Inline - * As an array of injection names, where the last item in the array is the function to call. - */ - -/** - * @ngdoc method - * @name $injector#get - * - * @description - * Return an instance of the service. - * - * @param {string} name The name of the instance to retrieve. - * @param {string=} caller An optional string to provide the origin of the function call for error messages. - * @return {*} The instance. - */ - -/** - * @ngdoc method - * @name $injector#invoke - * - * @description - * Invoke the method and supply the method arguments from the `$injector`. - * - * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are - * injected according to the {@link guide/di $inject Annotation} rules. - * @param {Object=} self The `this` for the invoked method. - * @param {Object=} locals Optional object. If preset then any argument names are read from this - * object first, before the `$injector` is consulted. - * @returns {*} the value returned by the invoked `fn` function. - */ - -/** - * @ngdoc method - * @name $injector#has - * - * @description - * Allows the user to query if the particular service exists. - * - * @param {string} name Name of the service to query. - * @returns {boolean} `true` if injector has given service. - */ - -/** - * @ngdoc method - * @name $injector#instantiate - * @description - * Create a new instance of JS type. The method takes a constructor function, invokes the new - * operator, and supplies all of the arguments to the constructor function as specified by the - * constructor annotation. - * - * @param {Function} Type Annotated constructor function. - * @param {Object=} locals Optional object. If preset then any argument names are read from this - * object first, before the `$injector` is consulted. - * @returns {Object} new instance of `Type`. - */ - -/** - * @ngdoc method - * @name $injector#annotate - * - * @description - * Returns an array of service names which the function is requesting for injection. This API is - * used by the injector to determine which services need to be injected into the function when the - * function is invoked. There are three ways in which the function can be annotated with the needed - * dependencies. - * - * # Argument names - * - * The simplest form is to extract the dependencies from the arguments of the function. This is done - * by converting the function into a string using `toString()` method and extracting the argument - * names. - * ```js - * // Given - * function MyController($scope, $route) { - * // ... - * } - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * ``` - * - * You can disallow this method by using strict injection mode. - * - * This method does not work with code minification / obfuscation. For this reason the following - * annotation strategies are supported. - * - * # The `$inject` property - * - * If a function has an `$inject` property and its value is an array of strings, then the strings - * represent names of services to be injected into the function. - * ```js - * // Given - * var MyController = function(obfuscatedScope, obfuscatedRoute) { - * // ... - * } - * // Define function dependencies - * MyController['$inject'] = ['$scope', '$route']; - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * ``` - * - * # The array notation - * - * It is often desirable to inline Injected functions and that's when setting the `$inject` property - * is very inconvenient. In these situations using the array notation to specify the dependencies in - * a way that survives minification is a better choice: - * - * ```js - * // We wish to write this (not minification / obfuscation safe) - * injector.invoke(function($compile, $rootScope) { - * // ... - * }); - * - * // We are forced to write break inlining - * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { - * // ... - * }; - * tmpFn.$inject = ['$compile', '$rootScope']; - * injector.invoke(tmpFn); - * - * // To better support inline function the inline annotation is supported - * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { - * // ... - * }]); - * - * // Therefore - * expect(injector.annotate( - * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) - * ).toEqual(['$compile', '$rootScope']); - * ``` - * - * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to - * be retrieved as described above. - * - * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. - * - * @returns {Array.<string>} The names of the services which the function requires. - */ - - - - -/** - * @ngdoc service - * @name $provide - * - * @description - * - * The {@link auto.$provide $provide} service has a number of methods for registering components - * with the {@link auto.$injector $injector}. Many of these functions are also exposed on - * {@link angular.Module}. - * - * An Angular **service** is a singleton object created by a **service factory**. These **service - * factories** are functions which, in turn, are created by a **service provider**. - * The **service providers** are constructor functions. When instantiated they must contain a - * property called `$get`, which holds the **service factory** function. - * - * When you request a service, the {@link auto.$injector $injector} is responsible for finding the - * correct **service provider**, instantiating it and then calling its `$get` **service factory** - * function to get the instance of the **service**. - * - * Often services have no configuration options and there is no need to add methods to the service - * provider. The provider will be no more than a constructor function with a `$get` property. For - * these cases the {@link auto.$provide $provide} service has additional helper methods to register - * services without specifying a provider. - * - * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the - * {@link auto.$injector $injector} - * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by - * providers and services. - * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by - * services, not providers. - * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, - * that will be wrapped in a **service provider** object, whose `$get` property will contain the - * given factory function. - * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` - * that will be wrapped in a **service provider** object, whose `$get` property will instantiate - * a new object using the given constructor function. - * - * See the individual methods for more information and examples. - */ - -/** - * @ngdoc method - * @name $provide#provider - * @description - * - * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions - * are constructor functions, whose instances are responsible for "providing" a factory for a - * service. - * - * Service provider names start with the name of the service they provide followed by `Provider`. - * For example, the {@link ng.$log $log} service has a provider called - * {@link ng.$logProvider $logProvider}. - * - * Service provider objects can have additional methods which allow configuration of the provider - * and its service. Importantly, you can configure what kind of service is created by the `$get` - * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a - * method {@link ng.$logProvider#debugEnabled debugEnabled} - * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the - * console or not. - * - * @param {string} name The name of the instance. NOTE: the provider will be available under `name + - 'Provider'` key. - * @param {(Object|function())} provider If the provider is: - * - * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using - * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. - * - `Constructor`: a new instance of the provider will be created using - * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. - * - * @returns {Object} registered provider instance - - * @example - * - * The following example shows how to create a simple event tracking service and register it using - * {@link auto.$provide#provider $provide.provider()}. - * - * ```js - * // Define the eventTracker provider - * function EventTrackerProvider() { - * var trackingUrl = '/track'; - * - * // A provider method for configuring where the tracked events should been saved - * this.setTrackingUrl = function(url) { - * trackingUrl = url; - * }; - * - * // The service factory function - * this.$get = ['$http', function($http) { - * var trackedEvents = {}; - * return { - * // Call this to track an event - * event: function(event) { - * var count = trackedEvents[event] || 0; - * count += 1; - * trackedEvents[event] = count; - * return count; - * }, - * // Call this to save the tracked events to the trackingUrl - * save: function() { - * $http.post(trackingUrl, trackedEvents); - * } - * }; - * }]; - * } - * - * describe('eventTracker', function() { - * var postSpy; - * - * beforeEach(module(function($provide) { - * // Register the eventTracker provider - * $provide.provider('eventTracker', EventTrackerProvider); - * })); - * - * beforeEach(module(function(eventTrackerProvider) { - * // Configure eventTracker provider - * eventTrackerProvider.setTrackingUrl('/custom-track'); - * })); - * - * it('tracks events', inject(function(eventTracker) { - * expect(eventTracker.event('login')).toEqual(1); - * expect(eventTracker.event('login')).toEqual(2); - * })); - * - * it('saves to the tracking url', inject(function(eventTracker, $http) { - * postSpy = spyOn($http, 'post'); - * eventTracker.event('login'); - * eventTracker.save(); - * expect(postSpy).toHaveBeenCalled(); - * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); - * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); - * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); - * })); - * }); - * ``` - */ - -/** - * @ngdoc method - * @name $provide#factory - * @description - * - * Register a **service factory**, which will be called to return the service instance. - * This is short for registering a service where its provider consists of only a `$get` property, - * which is the given service factory function. - * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to - * configure your service in a provider. - * - * @param {string} name The name of the instance. - * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation. - * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. - * @returns {Object} registered provider instance - * - * @example - * Here is an example of registering a service - * ```js - * $provide.factory('ping', ['$http', function($http) { - * return function ping() { - * return $http.send('/ping'); - * }; - * }]); - * ``` - * You would then inject and use this service like this: - * ```js - * someModule.controller('Ctrl', ['ping', function(ping) { - * ping(); - * }]); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#service - * @description - * - * Register a **service constructor**, which will be invoked with `new` to create the service - * instance. - * This is short for registering a service where its provider's `$get` property is the service - * constructor function that will be used to instantiate the service instance. - * - * You should use {@link auto.$provide#service $provide.service(class)} if you define your service - * as a type/class. - * - * @param {string} name The name of the instance. - * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function) - * that will be instantiated. - * @returns {Object} registered provider instance - * - * @example - * Here is an example of registering a service using - * {@link auto.$provide#service $provide.service(class)}. - * ```js - * var Ping = function($http) { - * this.$http = $http; - * }; - * - * Ping.$inject = ['$http']; - * - * Ping.prototype.send = function() { - * return this.$http.get('/ping'); - * }; - * $provide.service('ping', Ping); - * ``` - * You would then inject and use this service like this: - * ```js - * someModule.controller('Ctrl', ['ping', function(ping) { - * ping.send(); - * }]); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#value - * @description - * - * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a - * number, an array, an object or a function. This is short for registering a service where its - * provider's `$get` property is a factory function that takes no arguments and returns the **value - * service**. - * - * Value services are similar to constant services, except that they cannot be injected into a - * module configuration function (see {@link angular.Module#config}) but they can be overridden by - * an Angular - * {@link auto.$provide#decorator decorator}. - * - * @param {string} name The name of the instance. - * @param {*} value The value. - * @returns {Object} registered provider instance - * - * @example - * Here are some examples of creating value services. - * ```js - * $provide.value('ADMIN_USER', 'admin'); - * - * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); - * - * $provide.value('halfOf', function(value) { - * return value / 2; - * }); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#constant - * @description - * - * Register a **constant service**, such as a string, a number, an array, an object or a function, - * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be - * injected into a module configuration function (see {@link angular.Module#config}) and it cannot - * be overridden by an Angular {@link auto.$provide#decorator decorator}. - * - * @param {string} name The name of the constant. - * @param {*} value The constant value. - * @returns {Object} registered instance - * - * @example - * Here a some examples of creating constants: - * ```js - * $provide.constant('SHARD_HEIGHT', 306); - * - * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); - * - * $provide.constant('double', function(value) { - * return value * 2; - * }); - * ``` - */ - - -/** - * @ngdoc method - * @name $provide#decorator - * @description - * - * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator - * intercepts the creation of a service, allowing it to override or modify the behaviour of the - * service. The object returned by the decorator may be the original service, or a new service - * object which replaces or wraps and delegates to the original service. - * - * @param {string} name The name of the service to decorate. - * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be - * instantiated and should return the decorated service instance. The function is called using - * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. - * Local injection arguments: - * - * * `$delegate` - The original service instance, which can be monkey patched, configured, - * decorated or delegated to. - * - * @example - * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting - * calls to {@link ng.$log#error $log.warn()}. - * ```js - * $provide.decorator('$log', ['$delegate', function($delegate) { - * $delegate.warn = $delegate.error; - * return $delegate; - * }]); - * ``` - */ - - -function createInjector(modulesToLoad, strictDi) { - strictDi = (strictDi === true); - var INSTANTIATING = {}, - providerSuffix = 'Provider', - path = [], - loadedModules = new HashMap([], true), - providerCache = { - $provide: { - provider: supportObject(provider), - factory: supportObject(factory), - service: supportObject(service), - value: supportObject(value), - constant: supportObject(constant), - decorator: decorator - } - }, - providerInjector = (providerCache.$injector = - createInternalInjector(providerCache, function(serviceName, caller) { - if (angular.isString(caller)) { - path.push(caller); - } - throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); - })), - instanceCache = {}, - instanceInjector = (instanceCache.$injector = - createInternalInjector(instanceCache, function(serviceName, caller) { - var provider = providerInjector.get(serviceName + providerSuffix, caller); - return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); - })); - - - forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); }); - - return instanceInjector; - - //////////////////////////////////// - // $provider - //////////////////////////////////// - - function supportObject(delegate) { - return function(key, value) { - if (isObject(key)) { - forEach(key, reverseParams(delegate)); - } else { - return delegate(key, value); - } - }; - } - - function provider(name, provider_) { - assertNotHasOwnProperty(name, 'service'); - if (isFunction(provider_) || isArray(provider_)) { - provider_ = providerInjector.instantiate(provider_); - } - if (!provider_.$get) { - throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); - } - return providerCache[name + providerSuffix] = provider_; - } - - function enforceReturnValue(name, factory) { - return function enforcedReturnValue() { - var result = instanceInjector.invoke(factory, this); - if (isUndefined(result)) { - throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); - } - return result; - }; - } - - function factory(name, factoryFn, enforce) { - return provider(name, { - $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn - }); - } - - function service(name, constructor) { - return factory(name, ['$injector', function($injector) { - return $injector.instantiate(constructor); - }]); - } - - function value(name, val) { return factory(name, valueFn(val), false); } - - function constant(name, value) { - assertNotHasOwnProperty(name, 'constant'); - providerCache[name] = value; - instanceCache[name] = value; - } - - function decorator(serviceName, decorFn) { - var origProvider = providerInjector.get(serviceName + providerSuffix), - orig$get = origProvider.$get; - - origProvider.$get = function() { - var origInstance = instanceInjector.invoke(orig$get, origProvider); - return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); - }; - } - - //////////////////////////////////// - // Module Loading - //////////////////////////////////// - function loadModules(modulesToLoad) { - assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); - var runBlocks = [], moduleFn; - forEach(modulesToLoad, function(module) { - if (loadedModules.get(module)) return; - loadedModules.put(module, true); - - function runInvokeQueue(queue) { - var i, ii; - for (i = 0, ii = queue.length; i < ii; i++) { - var invokeArgs = queue[i], - provider = providerInjector.get(invokeArgs[0]); - - provider[invokeArgs[1]].apply(provider, invokeArgs[2]); - } - } - - try { - if (isString(module)) { - moduleFn = angularModule(module); - runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); - runInvokeQueue(moduleFn._invokeQueue); - runInvokeQueue(moduleFn._configBlocks); - } else if (isFunction(module)) { - runBlocks.push(providerInjector.invoke(module)); - } else if (isArray(module)) { - runBlocks.push(providerInjector.invoke(module)); - } else { - assertArgFn(module, 'module'); - } - } catch (e) { - if (isArray(module)) { - module = module[module.length - 1]; - } - if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { - // Safari & FF's stack traces don't contain error.message content - // unlike those of Chrome and IE - // So if stack doesn't contain message, we create a new string that contains both. - // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. - /* jshint -W022 */ - e = e.message + '\n' + e.stack; - } - throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", - module, e.stack || e.message || e); - } - }); - return runBlocks; - } - - //////////////////////////////////// - // internal Injector - //////////////////////////////////// - - function createInternalInjector(cache, factory) { - - function getService(serviceName, caller) { - if (cache.hasOwnProperty(serviceName)) { - if (cache[serviceName] === INSTANTIATING) { - throw $injectorMinErr('cdep', 'Circular dependency found: {0}', - serviceName + ' <- ' + path.join(' <- ')); - } - return cache[serviceName]; - } else { - try { - path.unshift(serviceName); - cache[serviceName] = INSTANTIATING; - return cache[serviceName] = factory(serviceName, caller); - } catch (err) { - if (cache[serviceName] === INSTANTIATING) { - delete cache[serviceName]; - } - throw err; - } finally { - path.shift(); - } - } - } - - function invoke(fn, self, locals, serviceName) { - if (typeof locals === 'string') { - serviceName = locals; - locals = null; - } - - var args = [], - $inject = createInjector.$$annotate(fn, strictDi, serviceName), - length, i, - key; - - for (i = 0, length = $inject.length; i < length; i++) { - key = $inject[i]; - if (typeof key !== 'string') { - throw $injectorMinErr('itkn', - 'Incorrect injection token! Expected service name as string, got {0}', key); - } - args.push( - locals && locals.hasOwnProperty(key) - ? locals[key] - : getService(key, serviceName) - ); - } - if (isArray(fn)) { - fn = fn[length]; - } - - // http://jsperf.com/angularjs-invoke-apply-vs-switch - // #5388 - return fn.apply(self, args); - } - - function instantiate(Type, locals, serviceName) { - // Check if Type is annotated and use just the given function at n-1 as parameter - // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); - // Object creation: http://jsperf.com/create-constructor/2 - var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null); - var returnedValue = invoke(Type, instance, locals, serviceName); - - return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; - } - - return { - invoke: invoke, - instantiate: instantiate, - get: getService, - annotate: createInjector.$$annotate, - has: function(name) { - return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); - } - }; - } -} - -createInjector.$$annotate = annotate; - -/** - * @ngdoc provider - * @name $anchorScrollProvider - * - * @description - * Use `$anchorScrollProvider` to disable automatic scrolling whenever - * {@link ng.$location#hash $location.hash()} changes. - */ -function $AnchorScrollProvider() { - - var autoScrollingEnabled = true; - - /** - * @ngdoc method - * @name $anchorScrollProvider#disableAutoScrolling - * - * @description - * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to - * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> - * Use this method to disable automatic scrolling. - * - * If automatic scrolling is disabled, one must explicitly call - * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the - * current hash. - */ - this.disableAutoScrolling = function() { - autoScrollingEnabled = false; - }; - - /** - * @ngdoc service - * @name $anchorScroll - * @kind function - * @requires $window - * @requires $location - * @requires $rootScope - * - * @description - * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the - * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified - * in the - * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). - * - * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to - * match any anchor whenever it changes. This can be disabled by calling - * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. - * - * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a - * vertical scroll-offset (either fixed or dynamic). - * - * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of - * {@link ng.$location#hash $location.hash()} will be used. - * - * @property {(number|function|jqLite)} yOffset - * If set, specifies a vertical scroll-offset. This is often useful when there are fixed - * positioned elements at the top of the page, such as navbars, headers etc. - * - * `yOffset` can be specified in various ways: - * - **number**: A fixed number of pixels to be used as offset.<br /><br /> - * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return - * a number representing the offset (in pixels).<br /><br /> - * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from - * the top of the page to the element's bottom will be used as offset.<br /> - * **Note**: The element will be taken into account only as long as its `position` is set to - * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust - * their height and/or positioning according to the viewport's size. - * - * <br /> - * <div class="alert alert-warning"> - * In order for `yOffset` to work properly, scrolling should take place on the document's root and - * not some child element. - * </div> - * - * @example - <example module="anchorScrollExample"> - <file name="index.html"> - <div id="scrollArea" ng-controller="ScrollController"> - <a ng-click="gotoBottom()">Go to bottom</a> - <a id="bottom"></a> You're at the bottom! - </div> - </file> - <file name="script.js"> - angular.module('anchorScrollExample', []) - .controller('ScrollController', ['$scope', '$location', '$anchorScroll', - function ($scope, $location, $anchorScroll) { - $scope.gotoBottom = function() { - // set the location.hash to the id of - // the element you wish to scroll to. - $location.hash('bottom'); - - // call $anchorScroll() - $anchorScroll(); - }; - }]); - </file> - <file name="style.css"> - #scrollArea { - height: 280px; - overflow: auto; - } - - #bottom { - display: block; - margin-top: 2000px; - } - </file> - </example> - * - * <hr /> - * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). - * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. - * - * @example - <example module="anchorScrollOffsetExample"> - <file name="index.html"> - <div class="fixed-header" ng-controller="headerCtrl"> - <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]"> - Go to anchor {{x}} - </a> - </div> - <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]"> - Anchor {{x}} of 5 - </div> - </file> - <file name="script.js"> - angular.module('anchorScrollOffsetExample', []) - .run(['$anchorScroll', function($anchorScroll) { - $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels - }]) - .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', - function ($anchorScroll, $location, $scope) { - $scope.gotoAnchor = function(x) { - var newHash = 'anchor' + x; - if ($location.hash() !== newHash) { - // set the $location.hash to `newHash` and - // $anchorScroll will automatically scroll to it - $location.hash('anchor' + x); - } else { - // call $anchorScroll() explicitly, - // since $location.hash hasn't changed - $anchorScroll(); - } - }; - } - ]); - </file> - <file name="style.css"> - body { - padding-top: 50px; - } - - .anchor { - border: 2px dashed DarkOrchid; - padding: 10px 10px 200px 10px; - } - - .fixed-header { - background-color: rgba(0, 0, 0, 0.2); - height: 50px; - position: fixed; - top: 0; left: 0; right: 0; - } - - .fixed-header > a { - display: inline-block; - margin: 5px 15px; - } - </file> - </example> - */ - this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { - var document = $window.document; - - // Helper function to get first anchor from a NodeList - // (using `Array#some()` instead of `angular#forEach()` since it's more performant - // and working in all supported browsers.) - function getFirstAnchor(list) { - var result = null; - Array.prototype.some.call(list, function(element) { - if (nodeName_(element) === 'a') { - result = element; - return true; - } - }); - return result; - } - - function getYOffset() { - - var offset = scroll.yOffset; - - if (isFunction(offset)) { - offset = offset(); - } else if (isElement(offset)) { - var elem = offset[0]; - var style = $window.getComputedStyle(elem); - if (style.position !== 'fixed') { - offset = 0; - } else { - offset = elem.getBoundingClientRect().bottom; - } - } else if (!isNumber(offset)) { - offset = 0; - } - - return offset; - } - - function scrollTo(elem) { - if (elem) { - elem.scrollIntoView(); - - var offset = getYOffset(); - - if (offset) { - // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. - // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the - // top of the viewport. - // - // IF the number of pixels from the top of `elem` to the end of the page's content is less - // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some - // way down the page. - // - // This is often the case for elements near the bottom of the page. - // - // In such cases we do not need to scroll the whole `offset` up, just the difference between - // the top of the element and the offset, which is enough to align the top of `elem` at the - // desired position. - var elemTop = elem.getBoundingClientRect().top; - $window.scrollBy(0, elemTop - offset); - } - } else { - $window.scrollTo(0, 0); - } - } - - function scroll(hash) { - hash = isString(hash) ? hash : $location.hash(); - var elm; - - // empty hash, scroll to the top of the page - if (!hash) scrollTo(null); - - // element with given id - else if ((elm = document.getElementById(hash))) scrollTo(elm); - - // first anchor with given name :-D - else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); - - // no element and hash == 'top', scroll to the top of the page - else if (hash === 'top') scrollTo(null); - } - - // does not scroll when user clicks on anchor link that is currently on - // (no url change, no $location.hash() change), browser native does scroll - if (autoScrollingEnabled) { - $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, - function autoScrollWatchAction(newVal, oldVal) { - // skip the initial scroll if $location.hash is empty - if (newVal === oldVal && newVal === '') return; - - jqLiteDocumentLoaded(function() { - $rootScope.$evalAsync(scroll); - }); - }); - } - - return scroll; - }]; -} - -var $animateMinErr = minErr('$animate'); -var ELEMENT_NODE = 1; -var NG_ANIMATE_CLASSNAME = 'ng-animate'; - -function mergeClasses(a,b) { - if (!a && !b) return ''; - if (!a) return b; - if (!b) return a; - if (isArray(a)) a = a.join(' '); - if (isArray(b)) b = b.join(' '); - return a + ' ' + b; -} - -function extractElementNode(element) { - for (var i = 0; i < element.length; i++) { - var elm = element[i]; - if (elm.nodeType === ELEMENT_NODE) { - return elm; - } - } -} - -function splitClasses(classes) { - if (isString(classes)) { - classes = classes.split(' '); - } - - // Use createMap() to prevent class assumptions involving property names in - // Object.prototype - var obj = createMap(); - forEach(classes, function(klass) { - // sometimes the split leaves empty string values - // incase extra spaces were applied to the options - if (klass.length) { - obj[klass] = true; - } - }); - return obj; -} - -// if any other type of options value besides an Object value is -// passed into the $animate.method() animation then this helper code -// will be run which will ignore it. While this patch is not the -// greatest solution to this, a lot of existing plugins depend on -// $animate to either call the callback (< 1.2) or return a promise -// that can be changed. This helper function ensures that the options -// are wiped clean incase a callback function is provided. -function prepareAnimateOptions(options) { - return isObject(options) - ? options - : {}; -} - -var $$CoreAnimateRunnerProvider = function() { - this.$get = ['$q', '$$rAF', function($q, $$rAF) { - function AnimateRunner() {} - AnimateRunner.all = noop; - AnimateRunner.chain = noop; - AnimateRunner.prototype = { - end: noop, - cancel: noop, - resume: noop, - pause: noop, - complete: noop, - then: function(pass, fail) { - return $q(function(resolve) { - $$rAF(function() { - resolve(); - }); - }).then(pass, fail); - } - }; - return AnimateRunner; - }]; -}; - -// this is prefixed with Core since it conflicts with -// the animateQueueProvider defined in ngAnimate/animateQueue.js -var $$CoreAnimateQueueProvider = function() { - var postDigestQueue = new HashMap(); - var postDigestElements = []; - - this.$get = ['$$AnimateRunner', '$rootScope', - function($$AnimateRunner, $rootScope) { - return { - enabled: noop, - on: noop, - off: noop, - pin: noop, - - push: function(element, event, options, domOperation) { - domOperation && domOperation(); - - options = options || {}; - options.from && element.css(options.from); - options.to && element.css(options.to); - - if (options.addClass || options.removeClass) { - addRemoveClassesPostDigest(element, options.addClass, options.removeClass); - } - - return new $$AnimateRunner(); // jshint ignore:line - } - }; - - - function updateData(data, classes, value) { - var changed = false; - if (classes) { - classes = isString(classes) ? classes.split(' ') : - isArray(classes) ? classes : []; - forEach(classes, function(className) { - if (className) { - changed = true; - data[className] = value; - } - }); - } - return changed; - } - - function handleCSSClassChanges() { - forEach(postDigestElements, function(element) { - var data = postDigestQueue.get(element); - if (data) { - var existing = splitClasses(element.attr('class')); - var toAdd = ''; - var toRemove = ''; - forEach(data, function(status, className) { - var hasClass = !!existing[className]; - if (status !== hasClass) { - if (status) { - toAdd += (toAdd.length ? ' ' : '') + className; - } else { - toRemove += (toRemove.length ? ' ' : '') + className; - } - } - }); - - forEach(element, function(elm) { - toAdd && jqLiteAddClass(elm, toAdd); - toRemove && jqLiteRemoveClass(elm, toRemove); - }); - postDigestQueue.remove(element); - } - }); - postDigestElements.length = 0; - } - - - function addRemoveClassesPostDigest(element, add, remove) { - var data = postDigestQueue.get(element) || {}; - - var classesAdded = updateData(data, add, true); - var classesRemoved = updateData(data, remove, false); - - if (classesAdded || classesRemoved) { - - postDigestQueue.put(element, data); - postDigestElements.push(element); - - if (postDigestElements.length === 1) { - $rootScope.$$postDigest(handleCSSClassChanges); - } - } - } - }]; -}; - -/** - * @ngdoc provider - * @name $animateProvider - * - * @description - * Default implementation of $animate that doesn't perform any animations, instead just - * synchronously performs DOM updates and resolves the returned runner promise. - * - * In order to enable animations the `ngAnimate` module has to be loaded. - * - * To see the functional implementation check out `src/ngAnimate/animate.js`. - */ -var $AnimateProvider = ['$provide', function($provide) { - var provider = this; - - this.$$registeredAnimations = Object.create(null); - - /** - * @ngdoc method - * @name $animateProvider#register - * - * @description - * Registers a new injectable animation factory function. The factory function produces the - * animation object which contains callback functions for each event that is expected to be - * animated. - * - * * `eventFn`: `function(element, ... , doneFunction, options)` - * The element to animate, the `doneFunction` and the options fed into the animation. Depending - * on the type of animation additional arguments will be injected into the animation function. The - * list below explains the function signatures for the different animation methods: - * - * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) - * - addClass: function(element, addedClasses, doneFunction, options) - * - removeClass: function(element, removedClasses, doneFunction, options) - * - enter, leave, move: function(element, doneFunction, options) - * - animate: function(element, fromStyles, toStyles, doneFunction, options) - * - * Make sure to trigger the `doneFunction` once the animation is fully complete. - * - * ```js - * return { - * //enter, leave, move signature - * eventFn : function(element, done, options) { - * //code to run the animation - * //once complete, then run done() - * return function endFunction(wasCancelled) { - * //code to cancel the animation - * } - * } - * } - * ``` - * - * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). - * @param {Function} factory The factory function that will be executed to return the animation - * object. - */ - this.register = function(name, factory) { - if (name && name.charAt(0) !== '.') { - throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); - } - - var key = name + '-animation'; - provider.$$registeredAnimations[name.substr(1)] = key; - $provide.factory(key, factory); - }; - - /** - * @ngdoc method - * @name $animateProvider#classNameFilter - * - * @description - * Sets and/or returns the CSS class regular expression that is checked when performing - * an animation. Upon bootstrap the classNameFilter value is not set at all and will - * therefore enable $animate to attempt to perform an animation on any element that is triggered. - * When setting the `classNameFilter` value, animations will only be performed on elements - * that successfully match the filter expression. This in turn can boost performance - * for low-powered devices as well as applications containing a lot of structural operations. - * @param {RegExp=} expression The className expression which will be checked against all animations - * @return {RegExp} The current CSS className expression value. If null then there is no expression value - */ - this.classNameFilter = function(expression) { - if (arguments.length === 1) { - this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; - if (this.$$classNameFilter) { - var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)"); - if (reservedRegex.test(this.$$classNameFilter.toString())) { - throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); - - } - } - } - return this.$$classNameFilter; - }; - - this.$get = ['$$animateQueue', function($$animateQueue) { - function domInsert(element, parentElement, afterElement) { - // if for some reason the previous element was removed - // from the dom sometime before this code runs then let's - // just stick to using the parent element as the anchor - if (afterElement) { - var afterNode = extractElementNode(afterElement); - if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { - afterElement = null; - } - } - afterElement ? afterElement.after(element) : parentElement.prepend(element); - } - - /** - * @ngdoc service - * @name $animate - * @description The $animate service exposes a series of DOM utility methods that provide support - * for animation hooks. The default behavior is the application of DOM operations, however, - * when an animation is detected (and animations are enabled), $animate will do the heavy lifting - * to ensure that animation runs with the triggered DOM operation. - * - * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't - * included and only when it is active then the animation hooks that `$animate` triggers will be - * functional. Once active then all structural `ng-` directives will trigger animations as they perform - * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, - * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. - * - * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. - * - * To learn more about enabling animation support, click here to visit the - * {@link ngAnimate ngAnimate module page}. - */ - return { - // we don't call it directly since non-existant arguments may - // be interpreted as null within the sub enabled function - - /** - * - * @ngdoc method - * @name $animate#on - * @kind function - * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) - * has fired on the given element or among any of its children. Once the listener is fired, the provided callback - * is fired with the following params: - * - * ```js - * $animate.on('enter', container, - * function callback(element, phase) { - * // cool we detected an enter animation within the container - * } - * ); - * ``` - * - * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself - * as well as among its children - * @param {Function} callback the callback function that will be fired when the listener is triggered - * - * The arguments present in the callback function are: - * * `element` - The captured DOM element that the animation was fired on. - * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). - */ - on: $$animateQueue.on, - - /** - * - * @ngdoc method - * @name $animate#off - * @kind function - * @description Deregisters an event listener based on the event which has been associated with the provided element. This method - * can be used in three different ways depending on the arguments: - * - * ```js - * // remove all the animation event listeners listening for `enter` - * $animate.off('enter'); - * - * // remove all the animation event listeners listening for `enter` on the given element and its children - * $animate.off('enter', container); - * - * // remove the event listener function provided by `listenerFn` that is set - * // to listen for `enter` on the given `element` as well as its children - * $animate.off('enter', container, callback); - * ``` - * - * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement=} container the container element the event listener was placed on - * @param {Function=} callback the callback function that was registered as the listener - */ - off: $$animateQueue.off, - - /** - * @ngdoc method - * @name $animate#pin - * @kind function - * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists - * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the - * element despite being outside the realm of the application or within another application. Say for example if the application - * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated - * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind - * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. - * - * Note that this feature is only active when the `ngAnimate` module is used. - * - * @param {DOMElement} element the external element that will be pinned - * @param {DOMElement} parentElement the host parent element that will be associated with the external element - */ - pin: $$animateQueue.pin, - - /** - * - * @ngdoc method - * @name $animate#enabled - * @kind function - * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This - * function can be called in four ways: - * - * ```js - * // returns true or false - * $animate.enabled(); - * - * // changes the enabled state for all animations - * $animate.enabled(false); - * $animate.enabled(true); - * - * // returns true or false if animations are enabled for an element - * $animate.enabled(element); - * - * // changes the enabled state for an element and its children - * $animate.enabled(element, true); - * $animate.enabled(element, false); - * ``` - * - * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state - * @param {boolean=} enabled whether or not the animations will be enabled for the element - * - * @return {boolean} whether or not animations are enabled - */ - enabled: $$animateQueue.enabled, - - /** - * @ngdoc method - * @name $animate#cancel - * @kind function - * @description Cancels the provided animation. - * - * @param {Promise} animationPromise The animation promise that is returned when an animation is started. - */ - cancel: function(runner) { - runner.end && runner.end(); - }, - - /** - * - * @ngdoc method - * @name $animate#enter - * @kind function - * @description Inserts the element into the DOM either after the `after` element (if provided) or - * as the first child within the `parent` element and then triggers an animation. - * A promise is returned that will be resolved during the next digest once the animation - * has completed. - * - * @param {DOMElement} element the element which will be inserted into the DOM - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - enter: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); - }, - - /** - * - * @ngdoc method - * @name $animate#move - * @kind function - * @description Inserts (moves) the element into its new position in the DOM either after - * the `after` element (if provided) or as the first child within the `parent` element - * and then triggers an animation. A promise is returned that will be resolved - * during the next digest once the animation has completed. - * - * @param {DOMElement} element the element which will be moved into the new DOM position - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - move: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); - }, - - /** - * @ngdoc method - * @name $animate#leave - * @kind function - * @description Triggers an animation and then removes the element from the DOM. - * When the function is called a promise is returned that will be resolved during the next - * digest once the animation has completed. - * - * @param {DOMElement} element the element which will be removed from the DOM - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - leave: function(element, options) { - return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { - element.remove(); - }); - }, - - /** - * @ngdoc method - * @name $animate#addClass - * @kind function - * - * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon - * execution, the addClass operation will only be handled after the next digest and it will not trigger an - * animation if element already contains the CSS class or if the class is removed at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - addClass: function(element, className, options) { - options = prepareAnimateOptions(options); - options.addClass = mergeClasses(options.addclass, className); - return $$animateQueue.push(element, 'addClass', options); - }, - - /** - * @ngdoc method - * @name $animate#removeClass - * @kind function - * - * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon - * execution, the removeClass operation will only be handled after the next digest and it will not trigger an - * animation if element does not contain the CSS class or if the class is added at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - removeClass: function(element, className, options) { - options = prepareAnimateOptions(options); - options.removeClass = mergeClasses(options.removeClass, className); - return $$animateQueue.push(element, 'removeClass', options); - }, - - /** - * @ngdoc method - * @name $animate#setClass - * @kind function - * - * @description Performs both the addition and removal of a CSS classes on an element and (during the process) - * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and - * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has - * passed. Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) - * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - setClass: function(element, add, remove, options) { - options = prepareAnimateOptions(options); - options.addClass = mergeClasses(options.addClass, add); - options.removeClass = mergeClasses(options.removeClass, remove); - return $$animateQueue.push(element, 'setClass', options); - }, - - /** - * @ngdoc method - * @name $animate#animate - * @kind function - * - * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. - * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take - * on the provided styles. For example, if a transition animation is set for the given className then the provided from and - * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles - * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter). - * - * @param {DOMElement} element the element which the CSS styles will be applied to - * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. - * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. - * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If - * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. - * (Note that if no animation is detected then this value will not be appplied to the element.) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - animate: function(element, from, to, className, options) { - options = prepareAnimateOptions(options); - options.from = options.from ? extend(options.from, from) : from; - options.to = options.to ? extend(options.to, to) : to; - - className = className || 'ng-inline-animate'; - options.tempClasses = mergeClasses(options.tempClasses, className); - return $$animateQueue.push(element, 'animate', options); - } - }; - }]; -}]; - -/** - * @ngdoc service - * @name $animateCss - * @kind object - * - * @description - * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, - * then the `$animateCss` service will actually perform animations. - * - * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. - */ -var $CoreAnimateCssProvider = function() { - this.$get = ['$$rAF', '$q', function($$rAF, $q) { - - var RAFPromise = function() {}; - RAFPromise.prototype = { - done: function(cancel) { - this.defer && this.defer[cancel === true ? 'reject' : 'resolve'](); - }, - end: function() { - this.done(); - }, - cancel: function() { - this.done(true); - }, - getPromise: function() { - if (!this.defer) { - this.defer = $q.defer(); - } - return this.defer.promise; - }, - then: function(f1,f2) { - return this.getPromise().then(f1,f2); - }, - 'catch': function(f1) { - return this.getPromise()['catch'](f1); - }, - 'finally': function(f1) { - return this.getPromise()['finally'](f1); - } - }; - - return function(element, options) { - // there is no point in applying the styles since - // there is no animation that goes on at all in - // this version of $animateCss. - if (options.cleanupStyles) { - options.from = options.to = null; - } - - if (options.from) { - element.css(options.from); - options.from = null; - } - - var closed, runner = new RAFPromise(); - return { - start: run, - end: run - }; - - function run() { - $$rAF(function() { - close(); - if (!closed) { - runner.done(); - } - closed = true; - }); - return runner; - } - - function close() { - if (options.addClass) { - element.addClass(options.addClass); - options.addClass = null; - } - if (options.removeClass) { - element.removeClass(options.removeClass); - options.removeClass = null; - } - if (options.to) { - element.css(options.to); - options.to = null; - } - } - }; - }]; -}; - -/* global stripHash: true */ - -/** - * ! This is a private undocumented service ! - * - * @name $browser - * @requires $log - * @description - * This object has two goals: - * - * - hide all the global state in the browser caused by the window object - * - abstract away all the browser specific features and inconsistencies - * - * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` - * service, which can be used for convenient testing of the application without the interaction with - * the real browser apis. - */ -/** - * @param {object} window The global window object. - * @param {object} document jQuery wrapped document. - * @param {object} $log window.console or an object with the same interface. - * @param {object} $sniffer $sniffer service - */ -function Browser(window, document, $log, $sniffer) { - var self = this, - rawDocument = document[0], - location = window.location, - history = window.history, - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - pendingDeferIds = {}; - - self.isMock = false; - - var outstandingRequestCount = 0; - var outstandingRequestCallbacks = []; - - // TODO(vojta): remove this temporary api - self.$$completeOutstandingRequest = completeOutstandingRequest; - self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; - - /** - * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` - * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. - */ - function completeOutstandingRequest(fn) { - try { - fn.apply(null, sliceArgs(arguments, 1)); - } finally { - outstandingRequestCount--; - if (outstandingRequestCount === 0) { - while (outstandingRequestCallbacks.length) { - try { - outstandingRequestCallbacks.pop()(); - } catch (e) { - $log.error(e); - } - } - } - } - } - - function getHash(url) { - var index = url.indexOf('#'); - return index === -1 ? '' : url.substr(index); - } - - /** - * @private - * Note: this method is used only by scenario runner - * TODO(vojta): prefix this method with $$ ? - * @param {function()} callback Function that will be called when no outstanding request - */ - self.notifyWhenNoOutstandingRequests = function(callback) { - if (outstandingRequestCount === 0) { - callback(); - } else { - outstandingRequestCallbacks.push(callback); - } - }; - - ////////////////////////////////////////////////////////////// - // URL API - ////////////////////////////////////////////////////////////// - - var cachedState, lastHistoryState, - lastBrowserUrl = location.href, - baseElement = document.find('base'), - pendingLocation = null; - - cacheState(); - lastHistoryState = cachedState; - - /** - * @name $browser#url - * - * @description - * GETTER: - * Without any argument, this method just returns current value of location.href. - * - * SETTER: - * With at least one argument, this method sets url to new value. - * If html5 history api supported, pushState/replaceState is used, otherwise - * location.href/location.replace is used. - * Returns its own instance to allow chaining - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to change url. - * - * @param {string} url New url (when used as setter) - * @param {boolean=} replace Should new url replace current history record? - * @param {object=} state object to use with pushState/replaceState - */ - self.url = function(url, replace, state) { - // In modern browsers `history.state` is `null` by default; treating it separately - // from `undefined` would cause `$browser.url('/foo')` to change `history.state` - // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. - if (isUndefined(state)) { - state = null; - } - - // Android Browser BFCache causes location, history reference to become stale. - if (location !== window.location) location = window.location; - if (history !== window.history) history = window.history; - - // setter - if (url) { - var sameState = lastHistoryState === state; - - // Don't change anything if previous and current URLs and states match. This also prevents - // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. - // See https://github.com/angular/angular.js/commit/ffb2701 - if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { - return self; - } - var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); - lastBrowserUrl = url; - lastHistoryState = state; - // Don't use history API if only the hash changed - // due to a bug in IE10/IE11 which leads - // to not firing a `hashchange` nor `popstate` event - // in some cases (see #9143). - if ($sniffer.history && (!sameBase || !sameState)) { - history[replace ? 'replaceState' : 'pushState'](state, '', url); - cacheState(); - // Do the assignment again so that those two variables are referentially identical. - lastHistoryState = cachedState; - } else { - if (!sameBase || pendingLocation) { - pendingLocation = url; - } - if (replace) { - location.replace(url); - } else if (!sameBase) { - location.href = url; - } else { - location.hash = getHash(url); - } - if (location.href !== url) { - pendingLocation = url; - } - } - return self; - // getter - } else { - // - pendingLocation is needed as browsers don't allow to read out - // the new location.href if a reload happened or if there is a bug like in iOS 9 (see - // https://openradar.appspot.com/22186109). - // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 - return pendingLocation || location.href.replace(/%27/g,"'"); - } - }; - - /** - * @name $browser#state - * - * @description - * This method is a getter. - * - * Return history.state or null if history.state is undefined. - * - * @returns {object} state - */ - self.state = function() { - return cachedState; - }; - - var urlChangeListeners = [], - urlChangeInit = false; - - function cacheStateAndFireUrlChange() { - pendingLocation = null; - cacheState(); - fireUrlChange(); - } - - function getCurrentState() { - try { - return history.state; - } catch (e) { - // MSIE can reportedly throw when there is no state (UNCONFIRMED). - } - } - - // This variable should be used *only* inside the cacheState function. - var lastCachedState = null; - function cacheState() { - // This should be the only place in $browser where `history.state` is read. - cachedState = getCurrentState(); - cachedState = isUndefined(cachedState) ? null : cachedState; - - // Prevent callbacks fo fire twice if both hashchange & popstate were fired. - if (equals(cachedState, lastCachedState)) { - cachedState = lastCachedState; - } - lastCachedState = cachedState; - } - - function fireUrlChange() { - if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { - return; - } - - lastBrowserUrl = self.url(); - lastHistoryState = cachedState; - forEach(urlChangeListeners, function(listener) { - listener(self.url(), cachedState); - }); - } - - /** - * @name $browser#onUrlChange - * - * @description - * Register callback function that will be called, when url changes. - * - * It's only called when the url is changed from outside of angular: - * - user types different url into address bar - * - user clicks on history (forward/back) button - * - user clicks on a link - * - * It's not called when url is changed by $browser.url() method - * - * The listener gets called with new url as parameter. - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to monitor url changes in angular apps. - * - * @param {function(string)} listener Listener function to be called when url changes. - * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. - */ - self.onUrlChange = function(callback) { - // TODO(vojta): refactor to use node's syntax for events - if (!urlChangeInit) { - // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) - // don't fire popstate when user change the address bar and don't fire hashchange when url - // changed by push/replaceState - - // html5 history api - popstate event - if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); - // hashchange event - jqLite(window).on('hashchange', cacheStateAndFireUrlChange); - - urlChangeInit = true; - } - - urlChangeListeners.push(callback); - return callback; - }; - - /** - * @private - * Remove popstate and hashchange handler from window. - * - * NOTE: this api is intended for use only by $rootScope. - */ - self.$$applicationDestroyed = function() { - jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange); - }; - - /** - * Checks whether the url has changed outside of Angular. - * Needs to be exported to be able to check for changes that have been done in sync, - * as hashchange/popstate events fire in async. - */ - self.$$checkUrlChange = fireUrlChange; - - ////////////////////////////////////////////////////////////// - // Misc API - ////////////////////////////////////////////////////////////// - - /** - * @name $browser#baseHref - * - * @description - * Returns current <base href> - * (always relative - without domain) - * - * @returns {string} The current base href - */ - self.baseHref = function() { - var href = baseElement.attr('href'); - return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; - }; - - /** - * @name $browser#defer - * @param {function()} fn A function, who's execution should be deferred. - * @param {number=} [delay=0] of milliseconds to defer the function execution. - * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. - * - * @description - * Executes a fn asynchronously via `setTimeout(fn, delay)`. - * - * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using - * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed - * via `$browser.defer.flush()`. - * - */ - self.defer = function(fn, delay) { - var timeoutId; - outstandingRequestCount++; - timeoutId = setTimeout(function() { - delete pendingDeferIds[timeoutId]; - completeOutstandingRequest(fn); - }, delay || 0); - pendingDeferIds[timeoutId] = true; - return timeoutId; - }; - - - /** - * @name $browser#defer.cancel - * - * @description - * Cancels a deferred task identified with `deferId`. - * - * @param {*} deferId Token returned by the `$browser.defer` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - self.defer.cancel = function(deferId) { - if (pendingDeferIds[deferId]) { - delete pendingDeferIds[deferId]; - clearTimeout(deferId); - completeOutstandingRequest(noop); - return true; - } - return false; - }; - -} - -function $BrowserProvider() { - this.$get = ['$window', '$log', '$sniffer', '$document', - function($window, $log, $sniffer, $document) { - return new Browser($window, $document, $log, $sniffer); - }]; -} - -/** - * @ngdoc service - * @name $cacheFactory - * - * @description - * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to - * them. - * - * ```js - * - * var cache = $cacheFactory('cacheId'); - * expect($cacheFactory.get('cacheId')).toBe(cache); - * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); - * - * cache.put("key", "value"); - * cache.put("another key", "another value"); - * - * // We've specified no options on creation - * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); - * - * ``` - * - * - * @param {string} cacheId Name or id of the newly created cache. - * @param {object=} options Options object that specifies the cache behavior. Properties: - * - * - `{number=}` `capacity` — turns the cache into LRU cache. - * - * @returns {object} Newly created cache object with the following set of methods: - * - * - `{object}` `info()` — Returns id, size, and options of cache. - * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns - * it. - * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. - * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. - * - `{void}` `removeAll()` — Removes all cached values. - * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. - * - * @example - <example module="cacheExampleApp"> - <file name="index.html"> - <div ng-controller="CacheController"> - <input ng-model="newCacheKey" placeholder="Key"> - <input ng-model="newCacheValue" placeholder="Value"> - <button ng-click="put(newCacheKey, newCacheValue)">Cache</button> - - <p ng-if="keys.length">Cached Values</p> - <div ng-repeat="key in keys"> - <span ng-bind="key"></span> - <span>: </span> - <b ng-bind="cache.get(key)"></b> - </div> - - <p>Cache Info</p> - <div ng-repeat="(key, value) in cache.info()"> - <span ng-bind="key"></span> - <span>: </span> - <b ng-bind="value"></b> - </div> - </div> - </file> - <file name="script.js"> - angular.module('cacheExampleApp', []). - controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { - $scope.keys = []; - $scope.cache = $cacheFactory('cacheId'); - $scope.put = function(key, value) { - if (angular.isUndefined($scope.cache.get(key))) { - $scope.keys.push(key); - } - $scope.cache.put(key, angular.isUndefined(value) ? null : value); - }; - }]); - </file> - <file name="style.css"> - p { - margin: 10px 0 3px; - } - </file> - </example> - */ -function $CacheFactoryProvider() { - - this.$get = function() { - var caches = {}; - - function cacheFactory(cacheId, options) { - if (cacheId in caches) { - throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); - } - - var size = 0, - stats = extend({}, options, {id: cacheId}), - data = {}, - capacity = (options && options.capacity) || Number.MAX_VALUE, - lruHash = {}, - freshEnd = null, - staleEnd = null; - - /** - * @ngdoc type - * @name $cacheFactory.Cache - * - * @description - * A cache object used to store and retrieve data, primarily used by - * {@link $http $http} and the {@link ng.directive:script script} directive to cache - * templates and other data. - * - * ```js - * angular.module('superCache') - * .factory('superCache', ['$cacheFactory', function($cacheFactory) { - * return $cacheFactory('super-cache'); - * }]); - * ``` - * - * Example test: - * - * ```js - * it('should behave like a cache', inject(function(superCache) { - * superCache.put('key', 'value'); - * superCache.put('another key', 'another value'); - * - * expect(superCache.info()).toEqual({ - * id: 'super-cache', - * size: 2 - * }); - * - * superCache.remove('another key'); - * expect(superCache.get('another key')).toBeUndefined(); - * - * superCache.removeAll(); - * expect(superCache.info()).toEqual({ - * id: 'super-cache', - * size: 0 - * }); - * })); - * ``` - */ - return caches[cacheId] = { - - /** - * @ngdoc method - * @name $cacheFactory.Cache#put - * @kind function - * - * @description - * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be - * retrieved later, and incrementing the size of the cache if the key was not already - * present in the cache. If behaving like an LRU cache, it will also remove stale - * entries from the set. - * - * It will not insert undefined values into the cache. - * - * @param {string} key the key under which the cached data is stored. - * @param {*} value the value to store alongside the key. If it is undefined, the key - * will not be stored. - * @returns {*} the value stored. - */ - put: function(key, value) { - if (isUndefined(value)) return; - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); - - refresh(lruEntry); - } - - if (!(key in data)) size++; - data[key] = value; - - if (size > capacity) { - this.remove(staleEnd.key); - } - - return value; - }, - - /** - * @ngdoc method - * @name $cacheFactory.Cache#get - * @kind function - * - * @description - * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. - * - * @param {string} key the key of the data to be retrieved - * @returns {*} the value stored. - */ - get: function(key) { - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - refresh(lruEntry); - } - - return data[key]; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#remove - * @kind function - * - * @description - * Removes an entry from the {@link $cacheFactory.Cache Cache} object. - * - * @param {string} key the key of the entry to be removed - */ - remove: function(key) { - if (capacity < Number.MAX_VALUE) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - if (lruEntry == freshEnd) freshEnd = lruEntry.p; - if (lruEntry == staleEnd) staleEnd = lruEntry.n; - link(lruEntry.n,lruEntry.p); - - delete lruHash[key]; - } - - delete data[key]; - size--; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#removeAll - * @kind function - * - * @description - * Clears the cache object of any entries. - */ - removeAll: function() { - data = {}; - size = 0; - lruHash = {}; - freshEnd = staleEnd = null; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#destroy - * @kind function - * - * @description - * Destroys the {@link $cacheFactory.Cache Cache} object entirely, - * removing it from the {@link $cacheFactory $cacheFactory} set. - */ - destroy: function() { - data = null; - stats = null; - lruHash = null; - delete caches[cacheId]; - }, - - - /** - * @ngdoc method - * @name $cacheFactory.Cache#info - * @kind function - * - * @description - * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. - * - * @returns {object} an object with the following properties: - * <ul> - * <li>**id**: the id of the cache instance</li> - * <li>**size**: the number of entries kept in the cache instance</li> - * <li>**...**: any additional properties from the options object when creating the - * cache.</li> - * </ul> - */ - info: function() { - return extend({}, stats, {size: size}); - } - }; - - - /** - * makes the `entry` the freshEnd of the LRU linked list - */ - function refresh(entry) { - if (entry != freshEnd) { - if (!staleEnd) { - staleEnd = entry; - } else if (staleEnd == entry) { - staleEnd = entry.n; - } - - link(entry.n, entry.p); - link(entry, freshEnd); - freshEnd = entry; - freshEnd.n = null; - } - } - - - /** - * bidirectionally links two entries of the LRU linked list - */ - function link(nextEntry, prevEntry) { - if (nextEntry != prevEntry) { - if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify - if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify - } - } - } - - - /** - * @ngdoc method - * @name $cacheFactory#info - * - * @description - * Get information about all the caches that have been created - * - * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` - */ - cacheFactory.info = function() { - var info = {}; - forEach(caches, function(cache, cacheId) { - info[cacheId] = cache.info(); - }); - return info; - }; - - - /** - * @ngdoc method - * @name $cacheFactory#get - * - * @description - * Get access to a cache object by the `cacheId` used when it was created. - * - * @param {string} cacheId Name or id of a cache to access. - * @returns {object} Cache object identified by the cacheId or undefined if no such cache. - */ - cacheFactory.get = function(cacheId) { - return caches[cacheId]; - }; - - - return cacheFactory; - }; -} - -/** - * @ngdoc service - * @name $templateCache - * - * @description - * The first time a template is used, it is loaded in the template cache for quick retrieval. You - * can load templates directly into the cache in a `script` tag, or by consuming the - * `$templateCache` service directly. - * - * Adding via the `script` tag: - * - * ```html - * <script type="text/ng-template" id="templateId.html"> - * <p>This is the content of the template</p> - * </script> - * ``` - * - * **Note:** the `script` tag containing the template does not need to be included in the `head` of - * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, - * element with ng-app attribute), otherwise the template will be ignored. - * - * Adding via the `$templateCache` service: - * - * ```js - * var myApp = angular.module('myApp', []); - * myApp.run(function($templateCache) { - * $templateCache.put('templateId.html', 'This is the content of the template'); - * }); - * ``` - * - * To retrieve the template later, simply use it in your HTML: - * ```html - * <div ng-include=" 'templateId.html' "></div> - * ``` - * - * or get it via Javascript: - * ```js - * $templateCache.get('templateId.html') - * ``` - * - * See {@link ng.$cacheFactory $cacheFactory}. - * - */ -function $TemplateCacheProvider() { - this.$get = ['$cacheFactory', function($cacheFactory) { - return $cacheFactory('templates'); - }]; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! - * - * DOM-related variables: - * - * - "node" - DOM Node - * - "element" - DOM Element or Node - * - "$node" or "$element" - jqLite-wrapped node or element - * - * - * Compiler related stuff: - * - * - "linkFn" - linking fn of a single directive - * - "nodeLinkFn" - function that aggregates all linking fns for a particular node - * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node - * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) - */ - - -/** - * @ngdoc service - * @name $compile - * @kind function - * - * @description - * Compiles an HTML string or DOM into a template and produces a template function, which - * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. - * - * The compilation is a process of walking the DOM tree and matching DOM elements to - * {@link ng.$compileProvider#directive directives}. - * - * <div class="alert alert-warning"> - * **Note:** This document is an in-depth reference of all directive options. - * For a gentle introduction to directives with examples of common use cases, - * see the {@link guide/directive directive guide}. - * </div> - * - * ## Comprehensive Directive API - * - * There are many different options for a directive. - * - * The difference resides in the return value of the factory function. - * You can either return a "Directive Definition Object" (see below) that defines the directive properties, - * or just the `postLink` function (all other properties will have the default values). - * - * <div class="alert alert-success"> - * **Best Practice:** It's recommended to use the "directive definition object" form. - * </div> - * - * Here's an example directive declared with a Directive Definition Object: - * - * ```js - * var myModule = angular.module(...); - * - * myModule.directive('directiveName', function factory(injectables) { - * var directiveDefinitionObject = { - * priority: 0, - * template: '<div></div>', // or // function(tElement, tAttrs) { ... }, - * // or - * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, - * transclude: false, - * restrict: 'A', - * templateNamespace: 'html', - * scope: false, - * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, - * controllerAs: 'stringIdentifier', - * bindToController: false, - * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], - * compile: function compile(tElement, tAttrs, transclude) { - * return { - * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, - * post: function postLink(scope, iElement, iAttrs, controller) { ... } - * } - * // or - * // return function postLink( ... ) { ... } - * }, - * // or - * // link: { - * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, - * // post: function postLink(scope, iElement, iAttrs, controller) { ... } - * // } - * // or - * // link: function postLink( ... ) { ... } - * }; - * return directiveDefinitionObject; - * }); - * ``` - * - * <div class="alert alert-warning"> - * **Note:** Any unspecified options will use the default value. You can see the default values below. - * </div> - * - * Therefore the above can be simplified as: - * - * ```js - * var myModule = angular.module(...); - * - * myModule.directive('directiveName', function factory(injectables) { - * var directiveDefinitionObject = { - * link: function postLink(scope, iElement, iAttrs) { ... } - * }; - * return directiveDefinitionObject; - * // or - * // return function postLink(scope, iElement, iAttrs) { ... } - * }); - * ``` - * - * - * - * ### Directive Definition Object - * - * The directive definition object provides instructions to the {@link ng.$compile - * compiler}. The attributes are: - * - * #### `multiElement` - * When this property is set to true, the HTML compiler will collect DOM nodes between - * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them - * together as the directive elements. It is recommended that this feature be used on directives - * which are not strictly behavioural (such as {@link ngClick}), and which - * do not manipulate or replace child nodes (such as {@link ngInclude}). - * - * #### `priority` - * When there are multiple directives defined on a single DOM element, sometimes it - * is necessary to specify the order in which the directives are applied. The `priority` is used - * to sort the directives before their `compile` functions get called. Priority is defined as a - * number. Directives with greater numerical `priority` are compiled first. Pre-link functions - * are also run in priority order, but post-link functions are run in reverse order. The order - * of directives with the same priority is undefined. The default priority is `0`. - * - * #### `terminal` - * If set to true then the current `priority` will be the last set of directives - * which will execute (any directives at the current priority will still execute - * as the order of execution on same `priority` is undefined). Note that expressions - * and other directives used in the directive's template will also be excluded from execution. - * - * #### `scope` - * The scope property can be `true`, an object or a falsy value: - * - * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope. - * - * * **`true`:** A new child scope that prototypically inherits from its parent will be created for - * the directive's element. If multiple directives on the same element request a new scope, - * only one new scope is created. The new scope rule does not apply for the root of the template - * since the root of the template always gets a new scope. - * - * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The - * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent - * scope. This is useful when creating reusable components, which should not accidentally read or modify - * data in the parent scope. - * - * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the - * directive's element. These local properties are useful for aliasing values for templates. The keys in - * the object hash map to the name of the property on the isolate scope; the values define how the property - * is bound to the parent scope, via matching attributes on the directive's element: - * - * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is - * always a string since DOM attributes are strings. If no `attr` name is specified then the - * attribute name is assumed to be the same as the local name. - * Given `<widget my-attr="hello {{name}}">` and widget definition - * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect - * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the - * `localName` property on the widget scope. The `name` is read from the parent scope (not - * component scope). - * - * * `=` or `=attr` - set up bi-directional binding between a local scope property and the - * parent scope property of name defined via the value of the `attr` attribute. If no `attr` - * name is specified then the attribute name is assumed to be the same as the local name. - * Given `<widget my-attr="parentModel">` and widget definition of - * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the - * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected - * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent - * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You - * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If - * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use - * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional). - * - * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. - * If no `attr` name is specified then the attribute name is assumed to be the same as the - * local name. Given `<widget my-attr="count = count + value">` and widget definition of - * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to - * a function wrapper for the `count = count + value` expression. Often it's desirable to - * pass data from the isolated scope via an expression to the parent scope, this can be - * done by passing a map of local variable names and values into the expression wrapper fn. - * For example, if the expression is `increment(amount)` then we can specify the amount value - * by calling the `localFn` as `localFn({amount: 22})`. - * - * In general it's possible to apply more than one directive to one element, but there might be limitations - * depending on the type of scope required by the directives. The following points will help explain these limitations. - * For simplicity only two directives are taken into account, but it is also applicable for several directives: - * - * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope - * * **child scope** + **no scope** => Both directives will share one single child scope - * * **child scope** + **child scope** => Both directives will share one single child scope - * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use - * its parent's scope - * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot - * be applied to the same element. - * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives - * cannot be applied to the same element. - * - * - * #### `bindToController` - * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will - * allow a component to have its properties bound to the controller, rather than to scope. When the controller - * is instantiated, the initial values of the isolate scope bindings are already available. - * - * #### `controller` - * Controller constructor function. The controller is instantiated before the - * pre-linking phase and can be accessed by other directives (see - * `require` attribute). This allows the directives to communicate with each other and augment - * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: - * - * * `$scope` - Current scope associated with the element - * * `$element` - Current element - * * `$attrs` - Current attributes object for the element - * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: - * `function([scope], cloneLinkingFn, futureParentElement)`. - * * `scope`: optional argument to override the scope. - * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content. - * * `futureParentElement`: - * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. - * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. - * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) - * and when the `cloneLinkinFn` is passed, - * as those elements need to created and cloned in a special way when they are defined outside their - * usual containers (e.g. like `<svg>`). - * * See also the `directive.templateNamespace` property. - * - * - * #### `require` - * Require another directive and inject its controller as the fourth argument to the linking function. The - * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the - * injected argument will be an array in corresponding order. If no such directive can be - * found, or if the directive does not have a controller, then an error is raised (unless no link function - * is specified, in which case error checking is skipped). The name can be prefixed with: - * - * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. - * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. - * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. - * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. - * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass - * `null` to the `link` fn if not found. - * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass - * `null` to the `link` fn if not found. - * - * - * #### `controllerAs` - * Identifier name for a reference to the controller in the directive's scope. - * This allows the controller to be referenced from the directive template. This is especially - * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible - * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the - * `controllerAs` reference might overwrite a property that already exists on the parent scope. - * - * - * #### `restrict` - * String of subset of `EACM` which restricts the directive to a specific directive - * declaration style. If omitted, the defaults (elements and attributes) are used. - * - * * `E` - Element name (default): `<my-directive></my-directive>` - * * `A` - Attribute (default): `<div my-directive="exp"></div>` - * * `C` - Class: `<div class="my-directive: exp;"></div>` - * * `M` - Comment: `<!-- directive: my-directive exp -->` - * - * - * #### `templateNamespace` - * String representing the document type used by the markup in the template. - * AngularJS needs this information as those elements need to be created and cloned - * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`. - * - * * `html` - All root nodes in the template are HTML. Root nodes may also be - * top-level elements such as `<svg>` or `<math>`. - * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`). - * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`). - * - * If no `templateNamespace` is specified, then the namespace is considered to be `html`. - * - * #### `template` - * HTML markup that may: - * * Replace the contents of the directive's element (default). - * * Replace the directive's element itself (if `replace` is true - DEPRECATED). - * * Wrap the contents of the directive's element (if `transclude` is true). - * - * Value may be: - * - * * A string. For example `<div red-on-hover>{{delete_str}}</div>`. - * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` - * function api below) and returns a string value. - * - * - * #### `templateUrl` - * This is similar to `template` but the template is loaded from the specified URL, asynchronously. - * - * Because template loading is asynchronous the compiler will suspend compilation of directives on that element - * for later when the template has been resolved. In the meantime it will continue to compile and link - * sibling and parent elements as though this element had not contained any directives. - * - * The compiler does not suspend the entire compilation to wait for templates to be loaded because this - * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the - * case when only one deeply nested directive has `templateUrl`. - * - * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} - * - * You can specify `templateUrl` as a string representing the URL or as a function which takes two - * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns - * a string value representing the url. In either case, the template URL is passed through {@link - * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. - * - * - * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) - * specify what the template should replace. Defaults to `false`. - * - * * `true` - the template will replace the directive's element. - * * `false` - the template will replace the contents of the directive's element. - * - * The replacement process migrates all of the attributes / classes from the old element to the new - * one. See the {@link guide/directive#template-expanding-directive - * Directives Guide} for an example. - * - * There are very few scenarios where element replacement is required for the application function, - * the main one being reusable custom components that are used within SVG contexts - * (because SVG doesn't work with custom elements in the DOM tree). - * - * #### `transclude` - * Extract the contents of the element where the directive appears and make it available to the directive. - * The contents are compiled and provided to the directive as a **transclusion function**. See the - * {@link $compile#transclusion Transclusion} section below. - * - * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the - * directive's element or the entire element: - * - * * `true` - transclude the content (i.e. the child nodes) of the directive's element. - * * `'element'` - transclude the whole of the directive's element including any directives on this - * element that defined at a lower priority than this directive. When used, the `template` - * property is ignored. - * - * - * #### `compile` - * - * ```js - * function compile(tElement, tAttrs, transclude) { ... } - * ``` - * - * The compile function deals with transforming the template DOM. Since most directives do not do - * template transformation, it is not used often. The compile function takes the following arguments: - * - * * `tElement` - template element - The element where the directive has been declared. It is - * safe to do template transformation on the element and child elements only. - * - * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared - * between all directive compile functions. - * - * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` - * - * <div class="alert alert-warning"> - * **Note:** The template instance and the link instance may be different objects if the template has - * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that - * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration - * should be done in a linking function rather than in a compile function. - * </div> - - * <div class="alert alert-warning"> - * **Note:** The compile function cannot handle directives that recursively use themselves in their - * own templates or compile functions. Compiling these directives results in an infinite loop and a - * stack overflow errors. - * - * This can be avoided by manually using $compile in the postLink function to imperatively compile - * a directive's template instead of relying on automatic template compilation via `template` or - * `templateUrl` declaration or manual compilation inside the compile function. - * </div> - * - * <div class="alert alert-danger"> - * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it - * e.g. does not know about the right outer scope. Please use the transclude function that is passed - * to the link function instead. - * </div> - - * A compile function can have a return value which can be either a function or an object. - * - * * returning a (post-link) function - is equivalent to registering the linking function via the - * `link` property of the config object when the compile function is empty. - * - * * returning an object with function(s) registered via `pre` and `post` properties - allows you to - * control when a linking function should be called during the linking phase. See info about - * pre-linking and post-linking functions below. - * - * - * #### `link` - * This property is used only if the `compile` property is not defined. - * - * ```js - * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } - * ``` - * - * The link function is responsible for registering DOM listeners as well as updating the DOM. It is - * executed after the template has been cloned. This is where most of the directive logic will be - * put. - * - * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the - * directive for registering {@link ng.$rootScope.Scope#$watch watches}. - * - * * `iElement` - instance element - The element where the directive is to be used. It is safe to - * manipulate the children of the element only in `postLink` function since the children have - * already been linked. - * - * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared - * between all directive linking functions. - * - * * `controller` - the directive's required controller instance(s) - Instances are shared - * among all directives, which allows the directives to use the controllers as a communication - * channel. The exact value depends on the directive's `require` property: - * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one - * * `string`: the controller instance - * * `array`: array of controller instances - * - * If a required controller cannot be found, and it is optional, the instance is `null`, - * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. - * - * Note that you can also require the directive's own controller - it will be made available like - * any other controller. - * - * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. - * This is the same as the `$transclude` - * parameter of directive controllers, see there for details. - * `function([scope], cloneLinkingFn, futureParentElement)`. - * - * #### Pre-linking function - * - * Executed before the child elements are linked. Not safe to do DOM transformation since the - * compiler linking function will fail to locate the correct elements for linking. - * - * #### Post-linking function - * - * Executed after the child elements are linked. - * - * Note that child elements that contain `templateUrl` directives will not have been compiled - * and linked since they are waiting for their template to load asynchronously and their own - * compilation and linking has been suspended until that occurs. - * - * It is safe to do DOM transformation in the post-linking function on elements that are not waiting - * for their async templates to be resolved. - * - * - * ### Transclusion - * - * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and - * copying them to another part of the DOM, while maintaining their connection to the original AngularJS - * scope from where they were taken. - * - * Transclusion is used (often with {@link ngTransclude}) to insert the - * original contents of a directive's element into a specified place in the template of the directive. - * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded - * content has access to the properties on the scope from which it was taken, even if the directive - * has isolated scope. - * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. - * - * This makes it possible for the widget to have private state for its template, while the transcluded - * content has access to its originating scope. - * - * <div class="alert alert-warning"> - * **Note:** When testing an element transclude directive you must not place the directive at the root of the - * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives - * Testing Transclusion Directives}. - * </div> - * - * #### Transclusion Functions - * - * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion - * function** to the directive's `link` function and `controller`. This transclusion function is a special - * **linking function** that will return the compiled contents linked to a new transclusion scope. - * - * <div class="alert alert-info"> - * If you are just using {@link ngTransclude} then you don't need to worry about this function, since - * ngTransclude will deal with it for us. - * </div> - * - * If you want to manually control the insertion and removal of the transcluded content in your directive - * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery - * object that contains the compiled DOM, which is linked to the correct transclusion scope. - * - * When you call a transclusion function you can pass in a **clone attach function**. This function accepts - * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded - * content and the `scope` is the newly created transclusion scope, to which the clone is bound. - * - * <div class="alert alert-info"> - * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function - * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. - * </div> - * - * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone - * attach function**: - * - * ```js - * var transcludedContent, transclusionScope; - * - * $transclude(function(clone, scope) { - * element.append(clone); - * transcludedContent = clone; - * transclusionScope = scope; - * }); - * ``` - * - * Later, if you want to remove the transcluded content from your DOM then you should also destroy the - * associated transclusion scope: - * - * ```js - * transcludedContent.remove(); - * transclusionScope.$destroy(); - * ``` - * - * <div class="alert alert-info"> - * **Best Practice**: if you intend to add and remove transcluded content manually in your directive - * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), - * then you are also responsible for calling `$destroy` on the transclusion scope. - * </div> - * - * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} - * automatically destroy their transluded clones as necessary so you do not need to worry about this if - * you are simply using {@link ngTransclude} to inject the transclusion into your directive. - * - * - * #### Transclusion Scopes - * - * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion - * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed - * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it - * was taken. - * - * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look - * like this: - * - * ```html - * <div ng-app> - * <div isolate> - * <div transclusion> - * </div> - * </div> - * </div> - * ``` - * - * The `$parent` scope hierarchy will look like this: - * - * ``` - * - $rootScope - * - isolate - * - transclusion - * ``` - * - * but the scopes will inherit prototypically from different scopes to their `$parent`. - * - * ``` - * - $rootScope - * - transclusion - * - isolate - * ``` - * - * - * ### Attributes - * - * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the - * `link()` or `compile()` functions. It has a variety of uses. - * - * accessing *Normalized attribute names:* - * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. - * the attributes object allows for normalized access to - * the attributes. - * - * * *Directive inter-communication:* All directives share the same instance of the attributes - * object which allows the directives to use the attributes object as inter directive - * communication. - * - * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object - * allowing other directives to read the interpolated value. - * - * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes - * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also - * the only way to easily get the actual value because during the linking phase the interpolation - * hasn't been evaluated yet and so the value is at this time set to `undefined`. - * - * ```js - * function linkingFn(scope, elm, attrs, ctrl) { - * // get the attribute value - * console.log(attrs.ngModel); - * - * // change the attribute - * attrs.$set('ngModel', 'new value'); - * - * // observe changes to interpolated attribute - * attrs.$observe('ngModel', function(value) { - * console.log('ngModel has changed value to ' + value); - * }); - * } - * ``` - * - * ## Example - * - * <div class="alert alert-warning"> - * **Note**: Typically directives are registered with `module.directive`. The example below is - * to illustrate how `$compile` works. - * </div> - * - <example module="compileExample"> - <file name="index.html"> - <script> - angular.module('compileExample', [], function($compileProvider) { - // configure new 'compile' directive by passing a directive - // factory function. The factory function injects the '$compile' - $compileProvider.directive('compile', function($compile) { - // directive factory creates a link function - return function(scope, element, attrs) { - scope.$watch( - function(scope) { - // watch the 'compile' expression for changes - return scope.$eval(attrs.compile); - }, - function(value) { - // when the 'compile' expression changes - // assign it into the current DOM - element.html(value); - - // compile the new DOM and link it to the current - // scope. - // NOTE: we only compile .childNodes so that - // we don't get into infinite loop compiling ourselves - $compile(element.contents())(scope); - } - ); - }; - }); - }) - .controller('GreeterController', ['$scope', function($scope) { - $scope.name = 'Angular'; - $scope.html = 'Hello {{name}}'; - }]); - </script> - <div ng-controller="GreeterController"> - <input ng-model="name"> <br/> - <textarea ng-model="html"></textarea> <br/> - <div compile="html"></div> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should auto compile', function() { - var textarea = $('textarea'); - var output = $('div[compile]'); - // The initial state reads 'Hello Angular'. - expect(output.getText()).toBe('Hello Angular'); - textarea.clear(); - textarea.sendKeys('{{name}}!'); - expect(output.getText()).toBe('Angular!'); - }); - </file> - </example> - - * - * - * @param {string|DOMElement} element Element or HTML string to compile into a template function. - * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. - * - * <div class="alert alert-danger"> - * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it - * e.g. will not use the right outer scope. Please pass the transclude function as a - * `parentBoundTranscludeFn` to the link function instead. - * </div> - * - * @param {number} maxPriority only apply directives lower than given priority (Only effects the - * root element(s), not their children) - * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template - * (a DOM element/tree) to a scope. Where: - * - * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. - * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the - * `template` and call the `cloneAttachFn` function allowing the caller to attach the - * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is - * called as: <br/> `cloneAttachFn(clonedElement, scope)` where: - * - * * `clonedElement` - is a clone of the original `element` passed into the compiler. - * * `scope` - is the current scope with which the linking function is working with. - * - * * `options` - An optional object hash with linking options. If `options` is provided, then the following - * keys may be used to control linking behavior: - * - * * `parentBoundTranscludeFn` - the transclude function made available to - * directives; if given, it will be passed through to the link functions of - * directives found in `element` during compilation. - * * `transcludeControllers` - an object hash with keys that map controller names - * to controller instances; if given, it will make the controllers - * available to directives. - * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add - * the cloned elements; only needed for transcludes that are allowed to contain non html - * elements (e.g. SVG elements). See also the directive.controller property. - * - * Calling the linking function returns the element of the template. It is either the original - * element passed in, or the clone of the element if the `cloneAttachFn` is provided. - * - * After linking the view is not updated until after a call to $digest which typically is done by - * Angular automatically. - * - * If you need access to the bound view, there are two ways to do it: - * - * - If you are not asking the linking function to clone the template, create the DOM element(s) - * before you send them to the compiler and keep this reference around. - * ```js - * var element = $compile('<p>{{total}}</p>')(scope); - * ``` - * - * - if on the other hand, you need the element to be cloned, the view reference from the original - * example would not point to the clone, but rather to the original template that was cloned. In - * this case, you can access the clone via the cloneAttachFn: - * ```js - * var templateElement = angular.element('<p>{{total}}</p>'), - * scope = ....; - * - * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { - * //attach the clone to DOM document at the right place - * }); - * - * //now we have reference to the cloned DOM via `clonedElement` - * ``` - * - * - * For information on how the compiler works, see the - * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. - */ - -var $compileMinErr = minErr('$compile'); - -/** - * @ngdoc provider - * @name $compileProvider - * - * @description - */ -$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; -function $CompileProvider($provide, $$sanitizeUriProvider) { - var hasDirectives = {}, - Suffix = 'Directive', - COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/, - CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/, - ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), - REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; - - // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes - // The assumption is that future DOM event attribute names will begin with - // 'on' and be composed of only English letters. - var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; - - function parseIsolateBindings(scope, directiveName, isController) { - var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; - - var bindings = {}; - - forEach(scope, function(definition, scopeName) { - var match = definition.match(LOCAL_REGEXP); - - if (!match) { - throw $compileMinErr('iscp', - "Invalid {3} for directive '{0}'." + - " Definition: {... {1}: '{2}' ...}", - directiveName, scopeName, definition, - (isController ? "controller bindings definition" : - "isolate scope definition")); - } - - bindings[scopeName] = { - mode: match[1][0], - collection: match[2] === '*', - optional: match[3] === '?', - attrName: match[4] || scopeName - }; - }); - - return bindings; - } - - function parseDirectiveBindings(directive, directiveName) { - var bindings = { - isolateScope: null, - bindToController: null - }; - if (isObject(directive.scope)) { - if (directive.bindToController === true) { - bindings.bindToController = parseIsolateBindings(directive.scope, - directiveName, true); - bindings.isolateScope = {}; - } else { - bindings.isolateScope = parseIsolateBindings(directive.scope, - directiveName, false); - } - } - if (isObject(directive.bindToController)) { - bindings.bindToController = - parseIsolateBindings(directive.bindToController, directiveName, true); - } - if (isObject(bindings.bindToController)) { - var controller = directive.controller; - var controllerAs = directive.controllerAs; - if (!controller) { - // There is no controller, there may or may not be a controllerAs property - throw $compileMinErr('noctrl', - "Cannot bind to controller without directive '{0}'s controller.", - directiveName); - } else if (!identifierForController(controller, controllerAs)) { - // There is a controller, but no identifier or controllerAs property - throw $compileMinErr('noident', - "Cannot bind to controller without identifier for directive '{0}'.", - directiveName); - } - } - return bindings; - } - - function assertValidDirectiveName(name) { - var letter = name.charAt(0); - if (!letter || letter !== lowercase(letter)) { - throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name); - } - if (name !== name.trim()) { - throw $compileMinErr('baddir', - "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces", - name); - } - } - - /** - * @ngdoc method - * @name $compileProvider#directive - * @kind function - * - * @description - * Register a new directive with the compiler. - * - * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which - * will match as <code>ng-bind</code>), or an object map of directives where the keys are the - * names and the values are the factories. - * @param {Function|Array} directiveFactory An injectable directive factory function. See - * {@link guide/directive} for more info. - * @returns {ng.$compileProvider} Self for chaining. - */ - this.directive = function registerDirective(name, directiveFactory) { - assertNotHasOwnProperty(name, 'directive'); - if (isString(name)) { - assertValidDirectiveName(name); - assertArg(directiveFactory, 'directiveFactory'); - if (!hasDirectives.hasOwnProperty(name)) { - hasDirectives[name] = []; - $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', - function($injector, $exceptionHandler) { - var directives = []; - forEach(hasDirectives[name], function(directiveFactory, index) { - try { - var directive = $injector.invoke(directiveFactory); - if (isFunction(directive)) { - directive = { compile: valueFn(directive) }; - } else if (!directive.compile && directive.link) { - directive.compile = valueFn(directive.link); - } - directive.priority = directive.priority || 0; - directive.index = index; - directive.name = directive.name || name; - directive.require = directive.require || (directive.controller && directive.name); - directive.restrict = directive.restrict || 'EA'; - var bindings = directive.$$bindings = - parseDirectiveBindings(directive, directive.name); - if (isObject(bindings.isolateScope)) { - directive.$$isolateBindings = bindings.isolateScope; - } - directive.$$moduleName = directiveFactory.$$moduleName; - directives.push(directive); - } catch (e) { - $exceptionHandler(e); - } - }); - return directives; - }]); - } - hasDirectives[name].push(directiveFactory); - } else { - forEach(name, reverseParams(registerDirective)); - } - return this; - }; - - - /** - * @ngdoc method - * @name $compileProvider#aHrefSanitizationWhitelist - * @kind function - * - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during a[href] sanitization. - * - * The sanitization is a security measure aimed at preventing XSS attacks via html links. - * - * Any url about to be assigned to a[href] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.aHrefSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); - return this; - } else { - return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); - } - }; - - - /** - * @ngdoc method - * @name $compileProvider#imgSrcSanitizationWhitelist - * @kind function - * - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during img[src] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to img[src] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.imgSrcSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); - return this; - } else { - return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); - } - }; - - /** - * @ngdoc method - * @name $compileProvider#debugInfoEnabled - * - * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the - * current debugInfoEnabled state - * @returns {*} current value if used as getter or itself (chaining) if used as setter - * - * @kind function - * - * @description - * Call this method to enable/disable various debug runtime information in the compiler such as adding - * binding information and a reference to the current scope on to DOM elements. - * If enabled, the compiler will add the following to DOM elements that have been bound to the scope - * * `ng-binding` CSS class - * * `$binding` data property containing an array of the binding expressions - * - * You may want to disable this in production for a significant performance boost. See - * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. - * - * The default value is true. - */ - var debugInfoEnabled = true; - this.debugInfoEnabled = function(enabled) { - if (isDefined(enabled)) { - debugInfoEnabled = enabled; - return this; - } - return debugInfoEnabled; - }; - - this.$get = [ - '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', - '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', - function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, - $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { - - var Attributes = function(element, attributesToCopy) { - if (attributesToCopy) { - var keys = Object.keys(attributesToCopy); - var i, l, key; - - for (i = 0, l = keys.length; i < l; i++) { - key = keys[i]; - this[key] = attributesToCopy[key]; - } - } else { - this.$attr = {}; - } - - this.$$element = element; - }; - - Attributes.prototype = { - /** - * @ngdoc method - * @name $compile.directive.Attributes#$normalize - * @kind function - * - * @description - * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or - * `data-`) to its normalized, camelCase form. - * - * Also there is special case for Moz prefix starting with upper case letter. - * - * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} - * - * @param {string} name Name to normalize - */ - $normalize: directiveNormalize, - - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$addClass - * @kind function - * - * @description - * Adds the CSS class value specified by the classVal parameter to the element. If animations - * are enabled then an animation will be triggered for the class addition. - * - * @param {string} classVal The className value that will be added to the element - */ - $addClass: function(classVal) { - if (classVal && classVal.length > 0) { - $animate.addClass(this.$$element, classVal); - } - }, - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$removeClass - * @kind function - * - * @description - * Removes the CSS class value specified by the classVal parameter from the element. If - * animations are enabled then an animation will be triggered for the class removal. - * - * @param {string} classVal The className value that will be removed from the element - */ - $removeClass: function(classVal) { - if (classVal && classVal.length > 0) { - $animate.removeClass(this.$$element, classVal); - } - }, - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$updateClass - * @kind function - * - * @description - * Adds and removes the appropriate CSS class values to the element based on the difference - * between the new and old CSS class values (specified as newClasses and oldClasses). - * - * @param {string} newClasses The current CSS className value - * @param {string} oldClasses The former CSS className value - */ - $updateClass: function(newClasses, oldClasses) { - var toAdd = tokenDifference(newClasses, oldClasses); - if (toAdd && toAdd.length) { - $animate.addClass(this.$$element, toAdd); - } - - var toRemove = tokenDifference(oldClasses, newClasses); - if (toRemove && toRemove.length) { - $animate.removeClass(this.$$element, toRemove); - } - }, - - /** - * Set a normalized attribute on the element in a way such that all directives - * can share the attribute. This function properly handles boolean attributes. - * @param {string} key Normalized key. (ie ngAttribute) - * @param {string|boolean} value The value to set. If `null` attribute will be deleted. - * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. - * Defaults to true. - * @param {string=} attrName Optional none normalized name. Defaults to key. - */ - $set: function(key, value, writeAttr, attrName) { - // TODO: decide whether or not to throw an error if "class" - //is set through this function since it may cause $updateClass to - //become unstable. - - var node = this.$$element[0], - booleanKey = getBooleanAttrName(node, key), - aliasedKey = getAliasedAttrName(key), - observer = key, - nodeName; - - if (booleanKey) { - this.$$element.prop(key, value); - attrName = booleanKey; - } else if (aliasedKey) { - this[aliasedKey] = value; - observer = aliasedKey; - } - - this[key] = value; - - // translate normalized key to actual key - if (attrName) { - this.$attr[key] = attrName; - } else { - attrName = this.$attr[key]; - if (!attrName) { - this.$attr[key] = attrName = snake_case(key, '-'); - } - } - - nodeName = nodeName_(this.$$element); - - if ((nodeName === 'a' && key === 'href') || - (nodeName === 'img' && key === 'src')) { - // sanitize a[href] and img[src] values - this[key] = value = $$sanitizeUri(value, key === 'src'); - } else if (nodeName === 'img' && key === 'srcset') { - // sanitize img[srcset] values - var result = ""; - - // first check if there are spaces because it's not the same pattern - var trimmedSrcset = trim(value); - // ( 999x ,| 999w ,| ,|, ) - var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; - var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; - - // split srcset into tuple of uri and descriptor except for the last item - var rawUris = trimmedSrcset.split(pattern); - - // for each tuples - var nbrUrisWith2parts = Math.floor(rawUris.length / 2); - for (var i = 0; i < nbrUrisWith2parts; i++) { - var innerIdx = i * 2; - // sanitize the uri - result += $$sanitizeUri(trim(rawUris[innerIdx]), true); - // add the descriptor - result += (" " + trim(rawUris[innerIdx + 1])); - } - - // split the last item into uri and descriptor - var lastTuple = trim(rawUris[i * 2]).split(/\s/); - - // sanitize the last uri - result += $$sanitizeUri(trim(lastTuple[0]), true); - - // and add the last descriptor if any - if (lastTuple.length === 2) { - result += (" " + trim(lastTuple[1])); - } - this[key] = value = result; - } - - if (writeAttr !== false) { - if (value === null || isUndefined(value)) { - this.$$element.removeAttr(attrName); - } else { - this.$$element.attr(attrName, value); - } - } - - // fire observers - var $$observers = this.$$observers; - $$observers && forEach($$observers[observer], function(fn) { - try { - fn(value); - } catch (e) { - $exceptionHandler(e); - } - }); - }, - - - /** - * @ngdoc method - * @name $compile.directive.Attributes#$observe - * @kind function - * - * @description - * Observes an interpolated attribute. - * - * The observer function will be invoked once during the next `$digest` following - * compilation. The observer is then invoked whenever the interpolated value - * changes. - * - * @param {string} key Normalized key. (ie ngAttribute) . - * @param {function(interpolatedValue)} fn Function that will be called whenever - the interpolated value of the attribute changes. - * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info. - * @returns {function()} Returns a deregistration function for this observer. - */ - $observe: function(key, fn) { - var attrs = this, - $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), - listeners = ($$observers[key] || ($$observers[key] = [])); - - listeners.push(fn); - $rootScope.$evalAsync(function() { - if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { - // no one registered attribute interpolation function, so lets call it manually - fn(attrs[key]); - } - }); - - return function() { - arrayRemove(listeners, fn); - }; - } - }; - - - function safeAddClass($element, className) { - try { - $element.addClass(className); - } catch (e) { - // ignore, since it means that we are trying to set class on - // SVG element, where class name is read-only. - } - } - - - var startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') - ? identity - : function denormalizeTemplate(template) { - return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); - }, - NG_ATTR_BINDING = /^ngAttr[A-Z]/; - - compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { - var bindings = $element.data('$binding') || []; - - if (isArray(binding)) { - bindings = bindings.concat(binding); - } else { - bindings.push(binding); - } - - $element.data('$binding', bindings); - } : noop; - - compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { - safeAddClass($element, 'ng-binding'); - } : noop; - - compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { - var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; - $element.data(dataName, scope); - } : noop; - - compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { - safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); - } : noop; - - return compile; - - //================================ - - function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, - previousCompileContext) { - if (!($compileNodes instanceof jqLite)) { - // jquery always rewraps, whereas we need to preserve the original selector so that we can - // modify it. - $compileNodes = jqLite($compileNodes); - } - // We can not compile top level text elements since text nodes can be merged and we will - // not be able to attach scope data to them, so we will wrap them in <span> - forEach($compileNodes, function(node, index) { - if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) { - $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; - } - }); - var compositeLinkFn = - compileNodes($compileNodes, transcludeFn, $compileNodes, - maxPriority, ignoreDirective, previousCompileContext); - compile.$$addScopeClass($compileNodes); - var namespace = null; - return function publicLinkFn(scope, cloneConnectFn, options) { - assertArg(scope, 'scope'); - - options = options || {}; - var parentBoundTranscludeFn = options.parentBoundTranscludeFn, - transcludeControllers = options.transcludeControllers, - futureParentElement = options.futureParentElement; - - // When `parentBoundTranscludeFn` is passed, it is a - // `controllersBoundTransclude` function (it was previously passed - // as `transclude` to directive.link) so we must unwrap it to get - // its `boundTranscludeFn` - if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { - parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; - } - - if (!namespace) { - namespace = detectNamespaceForChildElements(futureParentElement); - } - var $linkNode; - if (namespace !== 'html') { - // When using a directive with replace:true and templateUrl the $compileNodes - // (or a child element inside of them) - // might change, so we need to recreate the namespace adapted compileNodes - // for call to the link function. - // Note: This will already clone the nodes... - $linkNode = jqLite( - wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html()) - ); - } else if (cloneConnectFn) { - // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart - // and sometimes changes the structure of the DOM. - $linkNode = JQLitePrototype.clone.call($compileNodes); - } else { - $linkNode = $compileNodes; - } - - if (transcludeControllers) { - for (var controllerName in transcludeControllers) { - $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); - } - } - - compile.$$addScopeInfo($linkNode, scope); - - if (cloneConnectFn) cloneConnectFn($linkNode, scope); - if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); - return $linkNode; - }; - } - - function detectNamespaceForChildElements(parentElement) { - // TODO: Make this detect MathML as well... - var node = parentElement && parentElement[0]; - if (!node) { - return 'html'; - } else { - return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html'; - } - } - - /** - * Compile function matches each node in nodeList against the directives. Once all directives - * for a particular node are collected their compile functions are executed. The compile - * functions return values - the linking functions - are combined into a composite linking - * function, which is the a linking function for the node. - * - * @param {NodeList} nodeList an array of nodes or NodeList to compile - * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then - * the rootElement must be set the jqLite collection of the compile root. This is - * needed so that the jqLite collection items can be replaced with widgets. - * @param {number=} maxPriority Max directive priority. - * @returns {Function} A composite linking function of all of the matched directives or null. - */ - function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, - previousCompileContext) { - var linkFns = [], - attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; - - for (var i = 0; i < nodeList.length; i++) { - attrs = new Attributes(); - - // we must always refer to nodeList[i] since the nodes can be replaced underneath us. - directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, - ignoreDirective); - - nodeLinkFn = (directives.length) - ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, - null, [], [], previousCompileContext) - : null; - - if (nodeLinkFn && nodeLinkFn.scope) { - compile.$$addScopeClass(attrs.$$element); - } - - childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || - !(childNodes = nodeList[i].childNodes) || - !childNodes.length) - ? null - : compileNodes(childNodes, - nodeLinkFn ? ( - (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) - && nodeLinkFn.transclude) : transcludeFn); - - if (nodeLinkFn || childLinkFn) { - linkFns.push(i, nodeLinkFn, childLinkFn); - linkFnFound = true; - nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; - } - - //use the previous context only for the first element in the virtual group - previousCompileContext = null; - } - - // return a linking function if we have found anything, null otherwise - return linkFnFound ? compositeLinkFn : null; - - function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { - var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; - var stableNodeList; - - - if (nodeLinkFnFound) { - // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our - // offsets don't get screwed up - var nodeListLength = nodeList.length; - stableNodeList = new Array(nodeListLength); - - // create a sparse array by only copying the elements which have a linkFn - for (i = 0; i < linkFns.length; i+=3) { - idx = linkFns[i]; - stableNodeList[idx] = nodeList[idx]; - } - } else { - stableNodeList = nodeList; - } - - for (i = 0, ii = linkFns.length; i < ii;) { - node = stableNodeList[linkFns[i++]]; - nodeLinkFn = linkFns[i++]; - childLinkFn = linkFns[i++]; - - if (nodeLinkFn) { - if (nodeLinkFn.scope) { - childScope = scope.$new(); - compile.$$addScopeInfo(jqLite(node), childScope); - var destroyBindings = nodeLinkFn.$$destroyBindings; - if (destroyBindings) { - nodeLinkFn.$$destroyBindings = null; - childScope.$on('$destroyed', destroyBindings); - } - } else { - childScope = scope; - } - - if (nodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn( - scope, nodeLinkFn.transclude, parentBoundTranscludeFn); - - } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { - childBoundTranscludeFn = parentBoundTranscludeFn; - - } else if (!parentBoundTranscludeFn && transcludeFn) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); - - } else { - childBoundTranscludeFn = null; - } - - nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn, - nodeLinkFn); - - } else if (childLinkFn) { - childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); - } - } - } - } - - function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { - - var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { - - if (!transcludedScope) { - transcludedScope = scope.$new(false, containingScope); - transcludedScope.$$transcluded = true; - } - - return transcludeFn(transcludedScope, cloneFn, { - parentBoundTranscludeFn: previousBoundTranscludeFn, - transcludeControllers: controllers, - futureParentElement: futureParentElement - }); - }; - - return boundTranscludeFn; - } - - /** - * Looks for directives on the given node and adds them to the directive collection which is - * sorted. - * - * @param node Node to search. - * @param directives An array to which the directives are added to. This array is sorted before - * the function returns. - * @param attrs The shared attrs object which is used to populate the normalized attributes. - * @param {number=} maxPriority Max directive priority. - */ - function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { - var nodeType = node.nodeType, - attrsMap = attrs.$attr, - match, - className; - - switch (nodeType) { - case NODE_TYPE_ELEMENT: /* Element */ - // use the node name: <directive> - addDirective(directives, - directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); - - // iterate over the attributes - for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, - j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { - var attrStartName = false; - var attrEndName = false; - - attr = nAttrs[j]; - name = attr.name; - value = trim(attr.value); - - // support ngAttr attribute binding - ngAttrName = directiveNormalize(name); - if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { - name = name.replace(PREFIX_REGEXP, '') - .substr(8).replace(/_(.)/g, function(match, letter) { - return letter.toUpperCase(); - }); - } - - var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); - if (directiveIsMultiElement(directiveNName)) { - if (ngAttrName === directiveNName + 'Start') { - attrStartName = name; - attrEndName = name.substr(0, name.length - 5) + 'end'; - name = name.substr(0, name.length - 6); - } - } - - nName = directiveNormalize(name.toLowerCase()); - attrsMap[nName] = name; - if (isNgAttr || !attrs.hasOwnProperty(nName)) { - attrs[nName] = value; - if (getBooleanAttrName(node, nName)) { - attrs[nName] = true; // presence means true - } - } - addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); - addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, - attrEndName); - } - - // use class as directive - className = node.className; - if (isObject(className)) { - // Maybe SVGAnimatedString - className = className.animVal; - } - if (isString(className) && className !== '') { - while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { - nName = directiveNormalize(match[2]); - if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { - attrs[nName] = trim(match[3]); - } - className = className.substr(match.index + match[0].length); - } - } - break; - case NODE_TYPE_TEXT: /* Text Node */ - if (msie === 11) { - // Workaround for #11781 - while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) { - node.nodeValue = node.nodeValue + node.nextSibling.nodeValue; - node.parentNode.removeChild(node.nextSibling); - } - } - addTextInterpolateDirective(directives, node.nodeValue); - break; - case NODE_TYPE_COMMENT: /* Comment */ - try { - match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); - if (match) { - nName = directiveNormalize(match[1]); - if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { - attrs[nName] = trim(match[2]); - } - } - } catch (e) { - // turns out that under some circumstances IE9 throws errors when one attempts to read - // comment's node value. - // Just ignore it and continue. (Can't seem to reproduce in test case.) - } - break; - } - - directives.sort(byPriority); - return directives; - } - - /** - * Given a node with an directive-start it collects all of the siblings until it finds - * directive-end. - * @param node - * @param attrStart - * @param attrEnd - * @returns {*} - */ - function groupScan(node, attrStart, attrEnd) { - var nodes = []; - var depth = 0; - if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { - do { - if (!node) { - throw $compileMinErr('uterdir', - "Unterminated attribute, found '{0}' but no matching '{1}' found.", - attrStart, attrEnd); - } - if (node.nodeType == NODE_TYPE_ELEMENT) { - if (node.hasAttribute(attrStart)) depth++; - if (node.hasAttribute(attrEnd)) depth--; - } - nodes.push(node); - node = node.nextSibling; - } while (depth > 0); - } else { - nodes.push(node); - } - - return jqLite(nodes); - } - - /** - * Wrapper for linking function which converts normal linking function into a grouped - * linking function. - * @param linkFn - * @param attrStart - * @param attrEnd - * @returns {Function} - */ - function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { - return function(scope, element, attrs, controllers, transcludeFn) { - element = groupScan(element[0], attrStart, attrEnd); - return linkFn(scope, element, attrs, controllers, transcludeFn); - }; - } - - /** - * Once the directives have been collected, their compile functions are executed. This method - * is responsible for inlining directive templates as well as terminating the application - * of the directives if the terminal directive has been reached. - * - * @param {Array} directives Array of collected directives to execute their compile function. - * this needs to be pre-sorted by priority order. - * @param {Node} compileNode The raw DOM node to apply the compile functions to - * @param {Object} templateAttrs The shared attribute function - * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the - * scope argument is auto-generated to the new - * child of the transcluded parent scope. - * @param {JQLite} jqCollection If we are working on the root of the compile tree then this - * argument has the root jqLite array so that we can replace nodes - * on it. - * @param {Object=} originalReplaceDirective An optional directive that will be ignored when - * compiling the transclusion. - * @param {Array.<Function>} preLinkFns - * @param {Array.<Function>} postLinkFns - * @param {Object} previousCompileContext Context used for previous compilation of the current - * node - * @returns {Function} linkFn - */ - function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, - jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, - previousCompileContext) { - previousCompileContext = previousCompileContext || {}; - - var terminalPriority = -Number.MAX_VALUE, - newScopeDirective = previousCompileContext.newScopeDirective, - controllerDirectives = previousCompileContext.controllerDirectives, - newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, - templateDirective = previousCompileContext.templateDirective, - nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, - hasTranscludeDirective = false, - hasTemplate = false, - hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, - $compileNode = templateAttrs.$$element = jqLite(compileNode), - directive, - directiveName, - $template, - replaceDirective = originalReplaceDirective, - childTranscludeFn = transcludeFn, - linkFn, - directiveValue; - - // executes all directives on the current element - for (var i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - var attrStart = directive.$$start; - var attrEnd = directive.$$end; - - // collect multiblock sections - if (attrStart) { - $compileNode = groupScan(compileNode, attrStart, attrEnd); - } - $template = undefined; - - if (terminalPriority > directive.priority) { - break; // prevent further processing of directives - } - - if (directiveValue = directive.scope) { - - // skip the check for directives with async templates, we'll check the derived sync - // directive when the template arrives - if (!directive.templateUrl) { - if (isObject(directiveValue)) { - // This directive is trying to add an isolated scope. - // Check that there is no scope of any kind already - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, - directive, $compileNode); - newIsolateScopeDirective = directive; - } else { - // This directive is trying to add a child scope. - // Check that there is no isolated scope already - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, - $compileNode); - } - } - - newScopeDirective = newScopeDirective || directive; - } - - directiveName = directive.name; - - if (!directive.templateUrl && directive.controller) { - directiveValue = directive.controller; - controllerDirectives = controllerDirectives || createMap(); - assertNoDuplicate("'" + directiveName + "' controller", - controllerDirectives[directiveName], directive, $compileNode); - controllerDirectives[directiveName] = directive; - } - - if (directiveValue = directive.transclude) { - hasTranscludeDirective = true; - - // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. - // This option should only be used by directives that know how to safely handle element transclusion, - // where the transcluded nodes are added or replaced after linking. - if (!directive.$$tlb) { - assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); - nonTlbTranscludeDirective = directive; - } - - if (directiveValue == 'element') { - hasElementTranscludeDirective = true; - terminalPriority = directive.priority; - $template = $compileNode; - $compileNode = templateAttrs.$$element = - jqLite(document.createComment(' ' + directiveName + ': ' + - templateAttrs[directiveName] + ' ')); - compileNode = $compileNode[0]; - replaceWith(jqCollection, sliceArgs($template), compileNode); - - childTranscludeFn = compile($template, transcludeFn, terminalPriority, - replaceDirective && replaceDirective.name, { - // Don't pass in: - // - controllerDirectives - otherwise we'll create duplicates controllers - // - newIsolateScopeDirective or templateDirective - combining templates with - // element transclusion doesn't make sense. - // - // We need only nonTlbTranscludeDirective so that we prevent putting transclusion - // on the same element more than once. - nonTlbTranscludeDirective: nonTlbTranscludeDirective - }); - } else { - $template = jqLite(jqLiteClone(compileNode)).contents(); - $compileNode.empty(); // clear contents - childTranscludeFn = compile($template, transcludeFn); - } - } - - if (directive.template) { - hasTemplate = true; - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - - directiveValue = (isFunction(directive.template)) - ? directive.template($compileNode, templateAttrs) - : directive.template; - - directiveValue = denormalizeTemplate(directiveValue); - - if (directive.replace) { - replaceDirective = directive; - if (jqLiteIsTextNode(directiveValue)) { - $template = []; - } else { - $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); - } - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { - throw $compileMinErr('tplrt', - "Template for directive '{0}' must have exactly one root element. {1}", - directiveName, ''); - } - - replaceWith(jqCollection, $compileNode, compileNode); - - var newTemplateAttrs = {$attr: {}}; - - // combine directives from the original node and from the template: - // - take the array of directives for this element - // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) - // - collect directives from the template and sort them by priority - // - combine directives as: processed + template + unprocessed - var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); - var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); - - if (newIsolateScopeDirective) { - markDirectivesAsIsolate(templateDirectives); - } - directives = directives.concat(templateDirectives).concat(unprocessedDirectives); - mergeTemplateAttributes(templateAttrs, newTemplateAttrs); - - ii = directives.length; - } else { - $compileNode.html(directiveValue); - } - } - - if (directive.templateUrl) { - hasTemplate = true; - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - - if (directive.replace) { - replaceDirective = directive; - } - - nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, - templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { - controllerDirectives: controllerDirectives, - newScopeDirective: (newScopeDirective !== directive) && newScopeDirective, - newIsolateScopeDirective: newIsolateScopeDirective, - templateDirective: templateDirective, - nonTlbTranscludeDirective: nonTlbTranscludeDirective - }); - ii = directives.length; - } else if (directive.compile) { - try { - linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); - if (isFunction(linkFn)) { - addLinkFns(null, linkFn, attrStart, attrEnd); - } else if (linkFn) { - addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); - } - } catch (e) { - $exceptionHandler(e, startingTag($compileNode)); - } - } - - if (directive.terminal) { - nodeLinkFn.terminal = true; - terminalPriority = Math.max(terminalPriority, directive.priority); - } - - } - - nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; - nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; - nodeLinkFn.templateOnThisElement = hasTemplate; - nodeLinkFn.transclude = childTranscludeFn; - - previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; - - // might be normal or delayed nodeLinkFn depending on if templateUrl is present - return nodeLinkFn; - - //////////////////// - - function addLinkFns(pre, post, attrStart, attrEnd) { - if (pre) { - if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); - pre.require = directive.require; - pre.directiveName = directiveName; - if (newIsolateScopeDirective === directive || directive.$$isolateScope) { - pre = cloneAndAnnotateFn(pre, {isolateScope: true}); - } - preLinkFns.push(pre); - } - if (post) { - if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); - post.require = directive.require; - post.directiveName = directiveName; - if (newIsolateScopeDirective === directive || directive.$$isolateScope) { - post = cloneAndAnnotateFn(post, {isolateScope: true}); - } - postLinkFns.push(post); - } - } - - - function getControllers(directiveName, require, $element, elementControllers) { - var value; - - if (isString(require)) { - var match = require.match(REQUIRE_PREFIX_REGEXP); - var name = require.substring(match[0].length); - var inheritType = match[1] || match[3]; - var optional = match[2] === '?'; - - //If only parents then start at the parent element - if (inheritType === '^^') { - $element = $element.parent(); - //Otherwise attempt getting the controller from elementControllers in case - //the element is transcluded (and has no data) and to avoid .data if possible - } else { - value = elementControllers && elementControllers[name]; - value = value && value.instance; - } - - if (!value) { - var dataName = '$' + name + 'Controller'; - value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName); - } - - if (!value && !optional) { - throw $compileMinErr('ctreq', - "Controller '{0}', required by directive '{1}', can't be found!", - name, directiveName); - } - } else if (isArray(require)) { - value = []; - for (var i = 0, ii = require.length; i < ii; i++) { - value[i] = getControllers(directiveName, require[i], $element, elementControllers); - } - } - - return value || null; - } - - function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) { - var elementControllers = createMap(); - for (var controllerKey in controllerDirectives) { - var directive = controllerDirectives[controllerKey]; - var locals = { - $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, - $element: $element, - $attrs: attrs, - $transclude: transcludeFn - }; - - var controller = directive.controller; - if (controller == '@') { - controller = attrs[directive.name]; - } - - var controllerInstance = $controller(controller, locals, true, directive.controllerAs); - - // For directives with element transclusion the element is a comment, - // but jQuery .data doesn't support attaching data to comment nodes as it's hard to - // clean up (http://bugs.jquery.com/ticket/8335). - // Instead, we save the controllers for the element in a local hash and attach to .data - // later, once we have the actual element. - elementControllers[directive.name] = controllerInstance; - if (!hasElementTranscludeDirective) { - $element.data('$' + directive.name + 'Controller', controllerInstance.instance); - } - } - return elementControllers; - } - - function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn, - thisLinkFn) { - var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element, - attrs; - - if (compileNode === linkNode) { - attrs = templateAttrs; - $element = templateAttrs.$$element; - } else { - $element = jqLite(linkNode); - attrs = new Attributes($element, templateAttrs); - } - - if (newIsolateScopeDirective) { - isolateScope = scope.$new(true); - } - - if (boundTranscludeFn) { - // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` - // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` - transcludeFn = controllersBoundTransclude; - transcludeFn.$$boundTransclude = boundTranscludeFn; - } - - if (controllerDirectives) { - elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope); - } - - if (newIsolateScopeDirective) { - // Initialize isolate scope bindings for new isolate scope directive. - compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || - templateDirective === newIsolateScopeDirective.$$originalDirective))); - compile.$$addScopeClass($element, true); - isolateScope.$$isolateBindings = - newIsolateScopeDirective.$$isolateBindings; - initializeDirectiveBindings(scope, attrs, isolateScope, - isolateScope.$$isolateBindings, - newIsolateScopeDirective, isolateScope); - } - if (elementControllers) { - // Initialize bindToController bindings for new/isolate scopes - var scopeDirective = newIsolateScopeDirective || newScopeDirective; - var bindings; - var controllerForBindings; - if (scopeDirective && elementControllers[scopeDirective.name]) { - bindings = scopeDirective.$$bindings.bindToController; - controller = elementControllers[scopeDirective.name]; - - if (controller && controller.identifier && bindings) { - controllerForBindings = controller; - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controller.instance, - bindings, scopeDirective); - } - } - for (i in elementControllers) { - controller = elementControllers[i]; - var controllerResult = controller(); - - if (controllerResult !== controller.instance) { - // If the controller constructor has a return value, overwrite the instance - // from setupControllers and update the element data - controller.instance = controllerResult; - $element.data('$' + i + 'Controller', controllerResult); - if (controller === controllerForBindings) { - // Remove and re-install bindToController bindings - thisLinkFn.$$destroyBindings(); - thisLinkFn.$$destroyBindings = - initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective); - } - } - } - } - - // PRELINKING - for (i = 0, ii = preLinkFns.length; i < ii; i++) { - linkFn = preLinkFns[i]; - invokeLinkFn(linkFn, - linkFn.isolateScope ? isolateScope : scope, - $element, - attrs, - linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), - transcludeFn - ); - } - - // RECURSION - // We only pass the isolate scope, if the isolate directive has a template, - // otherwise the child elements do not belong to the isolate directive. - var scopeToChild = scope; - if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { - scopeToChild = isolateScope; - } - childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); - - // POSTLINKING - for (i = postLinkFns.length - 1; i >= 0; i--) { - linkFn = postLinkFns[i]; - invokeLinkFn(linkFn, - linkFn.isolateScope ? isolateScope : scope, - $element, - attrs, - linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), - transcludeFn - ); - } - - // This is the function that is injected as `$transclude`. - // Note: all arguments are optional! - function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) { - var transcludeControllers; - - // No scope passed in: - if (!isScope(scope)) { - futureParentElement = cloneAttachFn; - cloneAttachFn = scope; - scope = undefined; - } - - if (hasElementTranscludeDirective) { - transcludeControllers = elementControllers; - } - if (!futureParentElement) { - futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; - } - return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); - } - } - } - - function markDirectivesAsIsolate(directives) { - // mark all directives as needing isolate scope. - for (var j = 0, jj = directives.length; j < jj; j++) { - directives[j] = inherit(directives[j], {$$isolateScope: true}); - } - } - - /** - * looks up the directive and decorates it with exception handling and proper parameters. We - * call this the boundDirective. - * - * @param {string} name name of the directive to look up. - * @param {string} location The directive must be found in specific format. - * String containing any of theses characters: - * - * * `E`: element name - * * `A': attribute - * * `C`: class - * * `M`: comment - * @returns {boolean} true if directive was added. - */ - function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, - endAttrName) { - if (name === ignoreDirective) return null; - var match = null; - if (hasDirectives.hasOwnProperty(name)) { - for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i < ii; i++) { - try { - directive = directives[i]; - if ((isUndefined(maxPriority) || maxPriority > directive.priority) && - directive.restrict.indexOf(location) != -1) { - if (startAttrName) { - directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); - } - tDirectives.push(directive); - match = directive; - } - } catch (e) { $exceptionHandler(e); } - } - } - return match; - } - - - /** - * looks up the directive and returns true if it is a multi-element directive, - * and therefore requires DOM nodes between -start and -end markers to be grouped - * together. - * - * @param {string} name name of the directive to look up. - * @returns true if directive was registered as multi-element. - */ - function directiveIsMultiElement(name) { - if (hasDirectives.hasOwnProperty(name)) { - for (var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - if (directive.multiElement) { - return true; - } - } - } - return false; - } - - /** - * When the element is replaced with HTML template then the new attributes - * on the template need to be merged with the existing attributes in the DOM. - * The desired effect is to have both of the attributes present. - * - * @param {object} dst destination attributes (original DOM) - * @param {object} src source attributes (from the directive template) - */ - function mergeTemplateAttributes(dst, src) { - var srcAttr = src.$attr, - dstAttr = dst.$attr, - $element = dst.$$element; - - // reapply the old attributes to the new element - forEach(dst, function(value, key) { - if (key.charAt(0) != '$') { - if (src[key] && src[key] !== value) { - value += (key === 'style' ? ';' : ' ') + src[key]; - } - dst.$set(key, value, true, srcAttr[key]); - } - }); - - // copy the new attributes on the old attrs object - forEach(src, function(value, key) { - if (key == 'class') { - safeAddClass($element, value); - dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; - } else if (key == 'style') { - $element.attr('style', $element.attr('style') + ';' + value); - dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; - // `dst` will never contain hasOwnProperty as DOM parser won't let it. - // You will get an "InvalidCharacterError: DOM Exception 5" error if you - // have an attribute like "has-own-property" or "data-has-own-property", etc. - } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { - dst[key] = value; - dstAttr[key] = srcAttr[key]; - } - }); - } - - - function compileTemplateUrl(directives, $compileNode, tAttrs, - $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { - var linkQueue = [], - afterTemplateNodeLinkFn, - afterTemplateChildLinkFn, - beforeTemplateCompileNode = $compileNode[0], - origAsyncDirective = directives.shift(), - derivedSyncDirective = inherit(origAsyncDirective, { - templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective - }), - templateUrl = (isFunction(origAsyncDirective.templateUrl)) - ? origAsyncDirective.templateUrl($compileNode, tAttrs) - : origAsyncDirective.templateUrl, - templateNamespace = origAsyncDirective.templateNamespace; - - $compileNode.empty(); - - $templateRequest(templateUrl) - .then(function(content) { - var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; - - content = denormalizeTemplate(content); - - if (origAsyncDirective.replace) { - if (jqLiteIsTextNode(content)) { - $template = []; - } else { - $template = removeComments(wrapTemplate(templateNamespace, trim(content))); - } - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { - throw $compileMinErr('tplrt', - "Template for directive '{0}' must have exactly one root element. {1}", - origAsyncDirective.name, templateUrl); - } - - tempTemplateAttrs = {$attr: {}}; - replaceWith($rootElement, $compileNode, compileNode); - var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); - - if (isObject(origAsyncDirective.scope)) { - markDirectivesAsIsolate(templateDirectives); - } - directives = templateDirectives.concat(directives); - mergeTemplateAttributes(tAttrs, tempTemplateAttrs); - } else { - compileNode = beforeTemplateCompileNode; - $compileNode.html(content); - } - - directives.unshift(derivedSyncDirective); - - afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, - childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, - previousCompileContext); - forEach($rootElement, function(node, i) { - if (node == compileNode) { - $rootElement[i] = $compileNode[0]; - } - }); - afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); - - while (linkQueue.length) { - var scope = linkQueue.shift(), - beforeTemplateLinkNode = linkQueue.shift(), - linkRootElement = linkQueue.shift(), - boundTranscludeFn = linkQueue.shift(), - linkNode = $compileNode[0]; - - if (scope.$$destroyed) continue; - - if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { - var oldClasses = beforeTemplateLinkNode.className; - - if (!(previousCompileContext.hasElementTranscludeDirective && - origAsyncDirective.replace)) { - // it was cloned therefore we have to clone as well. - linkNode = jqLiteClone(compileNode); - } - replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); - - // Copy in CSS classes from original node - safeAddClass(jqLite(linkNode), oldClasses); - } - if (afterTemplateNodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); - } else { - childBoundTranscludeFn = boundTranscludeFn; - } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, - childBoundTranscludeFn, afterTemplateNodeLinkFn); - } - linkQueue = null; - }); - - return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { - var childBoundTranscludeFn = boundTranscludeFn; - if (scope.$$destroyed) return; - if (linkQueue) { - linkQueue.push(scope, - node, - rootElement, - childBoundTranscludeFn); - } else { - if (afterTemplateNodeLinkFn.transcludeOnThisElement) { - childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); - } - afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn, - afterTemplateNodeLinkFn); - } - }; - } - - - /** - * Sorting function for bound directives. - */ - function byPriority(a, b) { - var diff = b.priority - a.priority; - if (diff !== 0) return diff; - if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; - return a.index - b.index; - } - - function assertNoDuplicate(what, previousDirective, directive, element) { - - function wrapModuleNameIfDefined(moduleName) { - return moduleName ? - (' (module: ' + moduleName + ')') : - ''; - } - - if (previousDirective) { - throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}', - previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName), - directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element)); - } - } - - - function addTextInterpolateDirective(directives, text) { - var interpolateFn = $interpolate(text, true); - if (interpolateFn) { - directives.push({ - priority: 0, - compile: function textInterpolateCompileFn(templateNode) { - var templateNodeParent = templateNode.parent(), - hasCompileParent = !!templateNodeParent.length; - - // When transcluding a template that has bindings in the root - // we don't have a parent and thus need to add the class during linking fn. - if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); - - return function textInterpolateLinkFn(scope, node) { - var parent = node.parent(); - if (!hasCompileParent) compile.$$addBindingClass(parent); - compile.$$addBindingInfo(parent, interpolateFn.expressions); - scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { - node[0].nodeValue = value; - }); - }; - } - }); - } - } - - - function wrapTemplate(type, template) { - type = lowercase(type || 'html'); - switch (type) { - case 'svg': - case 'math': - var wrapper = document.createElement('div'); - wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>'; - return wrapper.childNodes[0].childNodes; - default: - return template; - } - } - - - function getTrustedContext(node, attrNormalizedName) { - if (attrNormalizedName == "srcdoc") { - return $sce.HTML; - } - var tag = nodeName_(node); - // maction[xlink:href] can source SVG. It's not limited to <maction>. - if (attrNormalizedName == "xlinkHref" || - (tag == "form" && attrNormalizedName == "action") || - (tag != "img" && (attrNormalizedName == "src" || - attrNormalizedName == "ngSrc"))) { - return $sce.RESOURCE_URL; - } - } - - - function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { - var trustedContext = getTrustedContext(node, name); - allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing; - - var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing); - - // no interpolation found -> ignore - if (!interpolateFn) return; - - - if (name === "multiple" && nodeName_(node) === "select") { - throw $compileMinErr("selmulti", - "Binding to the 'multiple' attribute is not supported. Element: {0}", - startingTag(node)); - } - - directives.push({ - priority: 100, - compile: function() { - return { - pre: function attrInterpolatePreLinkFn(scope, element, attr) { - var $$observers = (attr.$$observers || (attr.$$observers = createMap())); - - if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { - throw $compileMinErr('nodomevents', - "Interpolations for HTML DOM event attributes are disallowed. Please use the " + - "ng- versions (such as ng-click instead of onclick) instead."); - } - - // If the attribute has changed since last $interpolate()ed - var newValue = attr[name]; - if (newValue !== value) { - // we need to interpolate again since the attribute value has been updated - // (e.g. by another directive's compile function) - // ensure unset/empty values make interpolateFn falsy - interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); - value = newValue; - } - - // if attribute was updated so that there is no interpolation going on we don't want to - // register any observers - if (!interpolateFn) return; - - // initialize attr object so that it's ready in case we need the value for isolate - // scope initialization, otherwise the value would not be available from isolate - // directive's linking fn during linking phase - attr[name] = interpolateFn(scope); - - ($$observers[name] || ($$observers[name] = [])).$$inter = true; - (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { - //special case for class attribute addition + removal - //so that class changes can tap into the animation - //hooks provided by the $animate service. Be sure to - //skip animations when the first digest occurs (when - //both the new and the old values are the same) since - //the CSS classes are the non-interpolated values - if (name === 'class' && newValue != oldValue) { - attr.$updateClass(newValue, oldValue); - } else { - attr.$set(name, newValue); - } - }); - } - }; - } - }); - } - - - /** - * This is a special jqLite.replaceWith, which can replace items which - * have no parents, provided that the containing jqLite collection is provided. - * - * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep - * the shell, but replace its DOM node reference. - * @param {Node} newNode The new DOM node. - */ - function replaceWith($rootElement, elementsToRemove, newNode) { - var firstElementToRemove = elementsToRemove[0], - removeCount = elementsToRemove.length, - parent = firstElementToRemove.parentNode, - i, ii; - - if ($rootElement) { - for (i = 0, ii = $rootElement.length; i < ii; i++) { - if ($rootElement[i] == firstElementToRemove) { - $rootElement[i++] = newNode; - for (var j = i, j2 = j + removeCount - 1, - jj = $rootElement.length; - j < jj; j++, j2++) { - if (j2 < jj) { - $rootElement[j] = $rootElement[j2]; - } else { - delete $rootElement[j]; - } - } - $rootElement.length -= removeCount - 1; - - // If the replaced element is also the jQuery .context then replace it - // .context is a deprecated jQuery api, so we should set it only when jQuery set it - // http://api.jquery.com/context/ - if ($rootElement.context === firstElementToRemove) { - $rootElement.context = newNode; - } - break; - } - } - } - - if (parent) { - parent.replaceChild(newNode, firstElementToRemove); - } - - // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it? - var fragment = document.createDocumentFragment(); - fragment.appendChild(firstElementToRemove); - - if (jqLite.hasData(firstElementToRemove)) { - // Copy over user data (that includes Angular's $scope etc.). Don't copy private - // data here because there's no public interface in jQuery to do that and copying over - // event listeners (which is the main use of private data) wouldn't work anyway. - jqLite(newNode).data(jqLite(firstElementToRemove).data()); - - // Remove data of the replaced element. We cannot just call .remove() - // on the element it since that would deallocate scope that is needed - // for the new node. Instead, remove the data "manually". - if (!jQuery) { - delete jqLite.cache[firstElementToRemove[jqLite.expando]]; - } else { - // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after - // the replaced element. The cleanData version monkey-patched by Angular would cause - // the scope to be trashed and we do need the very same scope to work with the new - // element. However, we cannot just cache the non-patched version and use it here as - // that would break if another library patches the method after Angular does (one - // example is jQuery UI). Instead, set a flag indicating scope destroying should be - // skipped this one time. - skipDestroyOnNextJQueryCleanData = true; - jQuery.cleanData([firstElementToRemove]); - } - } - - for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { - var element = elementsToRemove[k]; - jqLite(element).remove(); // must do this way to clean up expando - fragment.appendChild(element); - delete elementsToRemove[k]; - } - - elementsToRemove[0] = newNode; - elementsToRemove.length = 1; - } - - - function cloneAndAnnotateFn(fn, annotation) { - return extend(function() { return fn.apply(null, arguments); }, fn, annotation); - } - - - function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { - try { - linkFn(scope, $element, attrs, controllers, transcludeFn); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - - - // Set up $watches for isolate scope and controller bindings. This process - // only occurs for isolate scopes and new scopes with controllerAs. - function initializeDirectiveBindings(scope, attrs, destination, bindings, - directive, newScope) { - var onNewScopeDestroyed; - forEach(bindings, function(definition, scopeName) { - var attrName = definition.attrName, - optional = definition.optional, - mode = definition.mode, // @, =, or & - lastValue, - parentGet, parentSet, compare; - - switch (mode) { - - case '@': - if (!optional && !hasOwnProperty.call(attrs, attrName)) { - destination[scopeName] = attrs[attrName] = void 0; - } - attrs.$observe(attrName, function(value) { - if (isString(value)) { - destination[scopeName] = value; - } - }); - attrs.$$observers[attrName].$$scope = scope; - if (isString(attrs[attrName])) { - // If the attribute has been provided then we trigger an interpolation to ensure - // the value is there for use in the link fn - destination[scopeName] = $interpolate(attrs[attrName])(scope); - } - break; - - case '=': - if (!hasOwnProperty.call(attrs, attrName)) { - if (optional) break; - attrs[attrName] = void 0; - } - if (optional && !attrs[attrName]) break; - - parentGet = $parse(attrs[attrName]); - if (parentGet.literal) { - compare = equals; - } else { - compare = function(a, b) { return a === b || (a !== a && b !== b); }; - } - parentSet = parentGet.assign || function() { - // reset the change, or we will throw this exception on every $digest - lastValue = destination[scopeName] = parentGet(scope); - throw $compileMinErr('nonassign', - "Expression '{0}' used with directive '{1}' is non-assignable!", - attrs[attrName], directive.name); - }; - lastValue = destination[scopeName] = parentGet(scope); - var parentValueWatch = function parentValueWatch(parentValue) { - if (!compare(parentValue, destination[scopeName])) { - // we are out of sync and need to copy - if (!compare(parentValue, lastValue)) { - // parent changed and it has precedence - destination[scopeName] = parentValue; - } else { - // if the parent can be assigned then do so - parentSet(scope, parentValue = destination[scopeName]); - } - } - return lastValue = parentValue; - }; - parentValueWatch.$stateful = true; - var unwatch; - if (definition.collection) { - unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); - } else { - unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); - } - onNewScopeDestroyed = (onNewScopeDestroyed || []); - onNewScopeDestroyed.push(unwatch); - break; - - case '&': - // Don't assign Object.prototype method to scope - parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; - - // Don't assign noop to destination if expression is not valid - if (parentGet === noop && optional) break; - - destination[scopeName] = function(locals) { - return parentGet(scope, locals); - }; - break; - } - }); - var destroyBindings = onNewScopeDestroyed ? function destroyBindings() { - for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) { - onNewScopeDestroyed[i](); - } - } : noop; - if (newScope && destroyBindings !== noop) { - newScope.$on('$destroy', destroyBindings); - return noop; - } - return destroyBindings; - } - }]; -} - -var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; -/** - * Converts all accepted directives format into proper directive name. - * @param name Name to normalize - */ -function directiveNormalize(name) { - return camelCase(name.replace(PREFIX_REGEXP, '')); -} - -/** - * @ngdoc type - * @name $compile.directive.Attributes - * - * @description - * A shared object between directive compile / linking functions which contains normalized DOM - * element attributes. The values reflect current binding state `{{ }}`. The normalization is - * needed since all of these are treated as equivalent in Angular: - * - * ``` - * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> - * ``` - */ - -/** - * @ngdoc property - * @name $compile.directive.Attributes#$attr - * - * @description - * A map of DOM element attribute names to the normalized name. This is - * needed to do reverse lookup from normalized name back to actual name. - */ - - -/** - * @ngdoc method - * @name $compile.directive.Attributes#$set - * @kind function - * - * @description - * Set DOM element attribute value. - * - * - * @param {string} name Normalized element attribute name of the property to modify. The name is - * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} - * property to the original name. - * @param {string} value Value to set the attribute to. The value can be an interpolated string. - */ - - - -/** - * Closure compiler type information - */ - -function nodesetLinkingFn( - /* angular.Scope */ scope, - /* NodeList */ nodeList, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -) {} - -function directiveLinkingFn( - /* nodesetLinkingFn */ nodesetLinkingFn, - /* angular.Scope */ scope, - /* Node */ node, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -) {} - -function tokenDifference(str1, str2) { - var values = '', - tokens1 = str1.split(/\s+/), - tokens2 = str2.split(/\s+/); - - outer: - for (var i = 0; i < tokens1.length; i++) { - var token = tokens1[i]; - for (var j = 0; j < tokens2.length; j++) { - if (token == tokens2[j]) continue outer; - } - values += (values.length > 0 ? ' ' : '') + token; - } - return values; -} - -function removeComments(jqNodes) { - jqNodes = jqLite(jqNodes); - var i = jqNodes.length; - - if (i <= 1) { - return jqNodes; - } - - while (i--) { - var node = jqNodes[i]; - if (node.nodeType === NODE_TYPE_COMMENT) { - splice.call(jqNodes, i, 1); - } - } - return jqNodes; -} - -var $controllerMinErr = minErr('$controller'); - - -var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; -function identifierForController(controller, ident) { - if (ident && isString(ident)) return ident; - if (isString(controller)) { - var match = CNTRL_REG.exec(controller); - if (match) return match[3]; - } -} - - -/** - * @ngdoc provider - * @name $controllerProvider - * @description - * The {@link ng.$controller $controller service} is used by Angular to create new - * controllers. - * - * This provider allows controller registration via the - * {@link ng.$controllerProvider#register register} method. - */ -function $ControllerProvider() { - var controllers = {}, - globals = false; - - /** - * @ngdoc method - * @name $controllerProvider#register - * @param {string|Object} name Controller name, or an object map of controllers where the keys are - * the names and the values are the constructors. - * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI - * annotations in the array notation). - */ - this.register = function(name, constructor) { - assertNotHasOwnProperty(name, 'controller'); - if (isObject(name)) { - extend(controllers, name); - } else { - controllers[name] = constructor; - } - }; - - /** - * @ngdoc method - * @name $controllerProvider#allowGlobals - * @description If called, allows `$controller` to find controller constructors on `window` - */ - this.allowGlobals = function() { - globals = true; - }; - - - this.$get = ['$injector', '$window', function($injector, $window) { - - /** - * @ngdoc service - * @name $controller - * @requires $injector - * - * @param {Function|string} constructor If called with a function then it's considered to be the - * controller constructor function. Otherwise it's considered to be a string which is used - * to retrieve the controller constructor using the following steps: - * - * * check if a controller with given name is registered via `$controllerProvider` - * * check if evaluating the string on the current scope returns a constructor - * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global - * `window` object (not recommended) - * - * The string can use the `controller as property` syntax, where the controller instance is published - * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this - * to work correctly. - * - * @param {Object} locals Injection locals for Controller. - * @return {Object} Instance of given controller. - * - * @description - * `$controller` service is responsible for instantiating controllers. - * - * It's just a simple call to {@link auto.$injector $injector}, but extracted into - * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). - */ - return function(expression, locals, later, ident) { - // PRIVATE API: - // param `later` --- indicates that the controller's constructor is invoked at a later time. - // If true, $controller will allocate the object with the correct - // prototype chain, but will not invoke the controller until a returned - // callback is invoked. - // param `ident` --- An optional label which overrides the label parsed from the controller - // expression, if any. - var instance, match, constructor, identifier; - later = later === true; - if (ident && isString(ident)) { - identifier = ident; - } - - if (isString(expression)) { - match = expression.match(CNTRL_REG); - if (!match) { - throw $controllerMinErr('ctrlfmt', - "Badly formed controller string '{0}'. " + - "Must match `__name__ as __id__` or `__name__`.", expression); - } - constructor = match[1], - identifier = identifier || match[3]; - expression = controllers.hasOwnProperty(constructor) - ? controllers[constructor] - : getter(locals.$scope, constructor, true) || - (globals ? getter($window, constructor, true) : undefined); - - assertArgFn(expression, constructor, true); - } - - if (later) { - // Instantiate controller later: - // This machinery is used to create an instance of the object before calling the - // controller's constructor itself. - // - // This allows properties to be added to the controller before the constructor is - // invoked. Primarily, this is used for isolate scope bindings in $compile. - // - // This feature is not intended for use by applications, and is thus not documented - // publicly. - // Object creation: http://jsperf.com/create-constructor/2 - var controllerPrototype = (isArray(expression) ? - expression[expression.length - 1] : expression).prototype; - instance = Object.create(controllerPrototype || null); - - if (identifier) { - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - - var instantiate; - return instantiate = extend(function() { - var result = $injector.invoke(expression, instance, locals, constructor); - if (result !== instance && (isObject(result) || isFunction(result))) { - instance = result; - if (identifier) { - // If result changed, re-assign controllerAs value to scope. - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - } - return instance; - }, { - instance: instance, - identifier: identifier - }); - } - - instance = $injector.instantiate(expression, locals, constructor); - - if (identifier) { - addIdentifier(locals, identifier, instance, constructor || expression.name); - } - - return instance; - }; - - function addIdentifier(locals, identifier, instance, name) { - if (!(locals && isObject(locals.$scope))) { - throw minErr('$controller')('noscp', - "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", - name, identifier); - } - - locals.$scope[identifier] = instance; - } - }]; -} - -/** - * @ngdoc service - * @name $document - * @requires $window - * - * @description - * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. - * - * @example - <example module="documentExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <p>$document title: <b ng-bind="title"></b></p> - <p>window.document title: <b ng-bind="windowTitle"></b></p> - </div> - </file> - <file name="script.js"> - angular.module('documentExample', []) - .controller('ExampleController', ['$scope', '$document', function($scope, $document) { - $scope.title = $document[0].title; - $scope.windowTitle = angular.element(window.document)[0].title; - }]); - </file> - </example> - */ -function $DocumentProvider() { - this.$get = ['$window', function(window) { - return jqLite(window.document); - }]; -} - -/** - * @ngdoc service - * @name $exceptionHandler - * @requires ng.$log - * - * @description - * Any uncaught exception in angular expressions is delegated to this service. - * The default implementation simply delegates to `$log.error` which logs it into - * the browser console. - * - * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by - * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. - * - * ## Example: - * - * ```js - * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() { - * return function(exception, cause) { - * exception.message += ' (caused by "' + cause + '")'; - * throw exception; - * }; - * }); - * ``` - * - * This example will override the normal action of `$exceptionHandler`, to make angular - * exceptions fail hard when they happen, instead of just logging to the console. - * - * <hr /> - * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` - * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} - * (unless executed during a digest). - * - * If you wish, you can manually delegate exceptions, e.g. - * `try { ... } catch(e) { $exceptionHandler(e); }` - * - * @param {Error} exception Exception associated with the error. - * @param {string=} cause optional information about the context in which - * the error was thrown. - * - */ -function $ExceptionHandlerProvider() { - this.$get = ['$log', function($log) { - return function(exception, cause) { - $log.error.apply($log, arguments); - }; - }]; -} - -var $$ForceReflowProvider = function() { - this.$get = ['$document', function($document) { - return function(domNode) { - //the line below will force the browser to perform a repaint so - //that all the animated elements within the animation frame will - //be properly updated and drawn on screen. This is required to - //ensure that the preparation animation is properly flushed so that - //the active state picks up from there. DO NOT REMOVE THIS LINE. - //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH - //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND - //WILL TAKE YEARS AWAY FROM YOUR LIFE. - if (domNode) { - if (!domNode.nodeType && domNode instanceof jqLite) { - domNode = domNode[0]; - } - } else { - domNode = $document[0].body; - } - return domNode.offsetWidth + 1; - }; - }]; -}; - -var APPLICATION_JSON = 'application/json'; -var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; -var JSON_START = /^\[|^\{(?!\{)/; -var JSON_ENDS = { - '[': /]$/, - '{': /}$/ -}; -var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; -var $httpMinErr = minErr('$http'); -var $httpMinErrLegacyFn = function(method) { - return function() { - throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method); - }; -}; - -function serializeValue(v) { - if (isObject(v)) { - return isDate(v) ? v.toISOString() : toJson(v); - } - return v; -} - - -function $HttpParamSerializerProvider() { - /** - * @ngdoc service - * @name $httpParamSerializer - * @description - * - * Default {@link $http `$http`} params serializer that converts objects to strings - * according to the following rules: - * - * * `{'foo': 'bar'}` results in `foo=bar` - * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object) - * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element) - * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object) - * - * Note that serializer will sort the request parameters alphabetically. - * */ - - this.$get = function() { - return function ngParamSerializer(params) { - if (!params) return ''; - var parts = []; - forEachSorted(params, function(value, key) { - if (value === null || isUndefined(value)) return; - if (isArray(value)) { - forEach(value, function(v, k) { - parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v))); - }); - } else { - parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value))); - } - }); - - return parts.join('&'); - }; - }; -} - -function $HttpParamSerializerJQLikeProvider() { - /** - * @ngdoc service - * @name $httpParamSerializerJQLike - * @description - * - * Alternative {@link $http `$http`} params serializer that follows - * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic. - * The serializer will also sort the params alphabetically. - * - * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property: - * - * ```js - * $http({ - * url: myUrl, - * method: 'GET', - * params: myParams, - * paramSerializer: '$httpParamSerializerJQLike' - * }); - * ``` - * - * It is also possible to set it as the default `paramSerializer` in the - * {@link $httpProvider#defaults `$httpProvider`}. - * - * Additionally, you can inject the serializer and use it explicitly, for example to serialize - * form data for submission: - * - * ```js - * .controller(function($http, $httpParamSerializerJQLike) { - * //... - * - * $http({ - * url: myUrl, - * method: 'POST', - * data: $httpParamSerializerJQLike(myData), - * headers: { - * 'Content-Type': 'application/x-www-form-urlencoded' - * } - * }); - * - * }); - * ``` - * - * */ - this.$get = function() { - return function jQueryLikeParamSerializer(params) { - if (!params) return ''; - var parts = []; - serialize(params, '', true); - return parts.join('&'); - - function serialize(toSerialize, prefix, topLevel) { - if (toSerialize === null || isUndefined(toSerialize)) return; - if (isArray(toSerialize)) { - forEach(toSerialize, function(value, index) { - serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); - }); - } else if (isObject(toSerialize) && !isDate(toSerialize)) { - forEachSorted(toSerialize, function(value, key) { - serialize(value, prefix + - (topLevel ? '' : '[') + - key + - (topLevel ? '' : ']')); - }); - } else { - parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); - } - } - }; - }; -} - -function defaultHttpResponseTransform(data, headers) { - if (isString(data)) { - // Strip json vulnerability protection prefix and trim whitespace - var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); - - if (tempData) { - var contentType = headers('Content-Type'); - if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { - data = fromJson(tempData); - } - } - } - - return data; -} - -function isJsonLike(str) { - var jsonStart = str.match(JSON_START); - return jsonStart && JSON_ENDS[jsonStart[0]].test(str); -} - -/** - * Parse headers into key value object - * - * @param {string} headers Raw headers as a string - * @returns {Object} Parsed headers as key value object - */ -function parseHeaders(headers) { - var parsed = createMap(), i; - - function fillInParsed(key, val) { - if (key) { - parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; - } - } - - if (isString(headers)) { - forEach(headers.split('\n'), function(line) { - i = line.indexOf(':'); - fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1))); - }); - } else if (isObject(headers)) { - forEach(headers, function(headerVal, headerKey) { - fillInParsed(lowercase(headerKey), trim(headerVal)); - }); - } - - return parsed; -} - - -/** - * Returns a function that provides access to parsed headers. - * - * Headers are lazy parsed when first requested. - * @see parseHeaders - * - * @param {(string|Object)} headers Headers to provide access to. - * @returns {function(string=)} Returns a getter function which if called with: - * - * - if called with single an argument returns a single header value or null - * - if called with no arguments returns an object containing all headers. - */ -function headersGetter(headers) { - var headersObj; - - return function(name) { - if (!headersObj) headersObj = parseHeaders(headers); - - if (name) { - var value = headersObj[lowercase(name)]; - if (value === void 0) { - value = null; - } - return value; - } - - return headersObj; - }; -} - - -/** - * Chain all given functions - * - * This function is used for both request and response transforming - * - * @param {*} data Data to transform. - * @param {function(string=)} headers HTTP headers getter fn. - * @param {number} status HTTP status code of the response. - * @param {(Function|Array.<Function>)} fns Function or an array of functions. - * @returns {*} Transformed data. - */ -function transformData(data, headers, status, fns) { - if (isFunction(fns)) { - return fns(data, headers, status); - } - - forEach(fns, function(fn) { - data = fn(data, headers, status); - }); - - return data; -} - - -function isSuccess(status) { - return 200 <= status && status < 300; -} - - -/** - * @ngdoc provider - * @name $httpProvider - * @description - * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. - * */ -function $HttpProvider() { - /** - * @ngdoc property - * @name $httpProvider#defaults - * @description - * - * Object containing default values for all {@link ng.$http $http} requests. - * - * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`} - * that will provide the cache for all requests who set their `cache` property to `true`. - * If you set the `defaults.cache = false` then only requests that specify their own custom - * cache object will be cached. See {@link $http#caching $http Caching} for more information. - * - * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. - * Defaults value is `'XSRF-TOKEN'`. - * - * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the - * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. - * - * - **`defaults.headers`** - {Object} - Default headers for all $http requests. - * Refer to {@link ng.$http#setting-http-headers $http} for documentation on - * setting default headers. - * - **`defaults.headers.common`** - * - **`defaults.headers.post`** - * - **`defaults.headers.put`** - * - **`defaults.headers.patch`** - * - * - * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function - * used to the prepare string representation of request parameters (specified as an object). - * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}. - * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}. - * - **/ - var defaults = this.defaults = { - // transform incoming response data - transformResponse: [defaultHttpResponseTransform], - - // transform outgoing request data - transformRequest: [function(d) { - return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; - }], - - // default headers - headers: { - common: { - 'Accept': 'application/json, text/plain, */*' - }, - post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), - put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), - patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) - }, - - xsrfCookieName: 'XSRF-TOKEN', - xsrfHeaderName: 'X-XSRF-TOKEN', - - paramSerializer: '$httpParamSerializer' - }; - - var useApplyAsync = false; - /** - * @ngdoc method - * @name $httpProvider#useApplyAsync - * @description - * - * Configure $http service to combine processing of multiple http responses received at around - * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in - * significant performance improvement for bigger applications that make many HTTP requests - * concurrently (common during application bootstrap). - * - * Defaults to false. If no value is specified, returns the current configured value. - * - * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred - * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window - * to load and share the same digest cycle. - * - * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. - * otherwise, returns the current configured value. - **/ - this.useApplyAsync = function(value) { - if (isDefined(value)) { - useApplyAsync = !!value; - return this; - } - return useApplyAsync; - }; - - var useLegacyPromise = true; - /** - * @ngdoc method - * @name $httpProvider#useLegacyPromiseExtensions - * @description - * - * Configure `$http` service to return promises without the shorthand methods `success` and `error`. - * This should be used to make sure that applications work without these methods. - * - * Defaults to false. If no value is specified, returns the current configured value. - * - * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods. - * - * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. - * otherwise, returns the current configured value. - **/ - this.useLegacyPromiseExtensions = function(value) { - if (isDefined(value)) { - useLegacyPromise = !!value; - return this; - } - return useLegacyPromise; - }; - - /** - * @ngdoc property - * @name $httpProvider#interceptors - * @description - * - * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} - * pre-processing of request or postprocessing of responses. - * - * These service factories are ordered by request, i.e. they are applied in the same order as the - * array, on request, but reverse order, on response. - * - * {@link ng.$http#interceptors Interceptors detailed info} - **/ - var interceptorFactories = this.interceptors = []; - - this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', - function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) { - - var defaultCache = $cacheFactory('$http'); - - /** - * Make sure that default param serializer is exposed as a function - */ - defaults.paramSerializer = isString(defaults.paramSerializer) ? - $injector.get(defaults.paramSerializer) : defaults.paramSerializer; - - /** - * Interceptors stored in reverse order. Inner interceptors before outer interceptors. - * The reversal is needed so that we can build up the interception chain around the - * server request. - */ - var reversedInterceptors = []; - - forEach(interceptorFactories, function(interceptorFactory) { - reversedInterceptors.unshift(isString(interceptorFactory) - ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); - }); - - /** - * @ngdoc service - * @kind function - * @name $http - * @requires ng.$httpBackend - * @requires $cacheFactory - * @requires $rootScope - * @requires $q - * @requires $injector - * - * @description - * The `$http` service is a core Angular service that facilitates communication with the remote - * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) - * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). - * - * For unit testing applications that use `$http` service, see - * {@link ngMock.$httpBackend $httpBackend mock}. - * - * For a higher level of abstraction, please check out the {@link ngResource.$resource - * $resource} service. - * - * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by - * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage - * it is important to familiarize yourself with these APIs and the guarantees they provide. - * - * - * ## General usage - * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} — - * that is used to generate an HTTP request and returns a {@link ng.$q promise}. - * - * ```js - * // Simple GET request example: - * $http({ - * method: 'GET', - * url: '/someUrl' - * }).then(function successCallback(response) { - * // this callback will be called asynchronously - * // when the response is available - * }, function errorCallback(response) { - * // called asynchronously if an error occurs - * // or server returns response with an error status. - * }); - * ``` - * - * The response object has these properties: - * - * - **data** – `{string|Object}` – The response body transformed with the transform - * functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - **statusText** – `{string}` – HTTP status text of the response. - * - * A response status code between 200 and 299 is considered a success status and - * will result in the success callback being called. Note that if the response is a redirect, - * XMLHttpRequest will transparently follow it, meaning that the error callback will not be - * called for such responses. - * - * - * ## Shortcut methods - * - * Shortcut methods are also available. All shortcut methods require passing in the URL, and - * request data must be passed in for POST/PUT requests. An optional config can be passed as the - * last argument. - * - * ```js - * $http.get('/someUrl', config).then(successCallback, errorCallback); - * $http.post('/someUrl', data, config).then(successCallback, errorCallback); - * ``` - * - * Complete list of shortcut methods: - * - * - {@link ng.$http#get $http.get} - * - {@link ng.$http#head $http.head} - * - {@link ng.$http#post $http.post} - * - {@link ng.$http#put $http.put} - * - {@link ng.$http#delete $http.delete} - * - {@link ng.$http#jsonp $http.jsonp} - * - {@link ng.$http#patch $http.patch} - * - * - * ## Writing Unit Tests that use $http - * When unit testing (using {@link ngMock ngMock}), it is necessary to call - * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending - * request using trained responses. - * - * ``` - * $httpBackend.expectGET(...); - * $http.get(...); - * $httpBackend.flush(); - * ``` - * - * ## Deprecation Notice - * <div class="alert alert-danger"> - * The `$http` legacy promise methods `success` and `error` have been deprecated. - * Use the standard `then` method instead. - * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to - * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error. - * </div> - * - * ## Setting HTTP Headers - * - * The $http service will automatically add certain HTTP headers to all requests. These defaults - * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration - * object, which currently contains this default configuration: - * - * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): - * - `Accept: application/json, text/plain, * / *` - * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) - * - `Content-Type: application/json` - * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) - * - `Content-Type: application/json` - * - * To add or overwrite these defaults, simply add or remove a property from these configuration - * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object - * with the lowercased HTTP method name as the key, e.g. - * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`. - * - * The defaults can also be set at runtime via the `$http.defaults` object in the same - * fashion. For example: - * - * ``` - * module.run(function($http) { - * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' - * }); - * ``` - * - * In addition, you can supply a `headers` property in the config object passed when - * calling `$http(config)`, which overrides the defaults without changing them globally. - * - * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, - * Use the `headers` property, setting the desired header to `undefined`. For example: - * - * ```js - * var req = { - * method: 'POST', - * url: 'http://example.com', - * headers: { - * 'Content-Type': undefined - * }, - * data: { test: 'test' } - * } - * - * $http(req).then(function(){...}, function(){...}); - * ``` - * - * ## Transforming Requests and Responses - * - * Both requests and responses can be transformed using transformation functions: `transformRequest` - * and `transformResponse`. These properties can be a single function that returns - * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, - * which allows you to `push` or `unshift` a new transformation function into the transformation chain. - * - * ### Default Transformations - * - * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and - * `defaults.transformResponse` properties. If a request does not provide its own transformations - * then these will be applied. - * - * You can augment or replace the default transformations by modifying these properties by adding to or - * replacing the array. - * - * Angular provides the following default transformations: - * - * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`): - * - * - If the `data` property of the request configuration object contains an object, serialize it - * into JSON format. - * - * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`): - * - * - If XSRF prefix is detected, strip it (see Security Considerations section below). - * - If JSON response is detected, deserialize it using a JSON parser. - * - * - * ### Overriding the Default Transformations Per Request - * - * If you wish override the request/response transformations only for a single request then provide - * `transformRequest` and/or `transformResponse` properties on the configuration object passed - * into `$http`. - * - * Note that if you provide these properties on the config object the default transformations will be - * overwritten. If you wish to augment the default transformations then you must include them in your - * local transformation array. - * - * The following code demonstrates adding a new response transformation to be run after the default response - * transformations have been run. - * - * ```js - * function appendTransform(defaults, transform) { - * - * // We can't guarantee that the default transformation is an array - * defaults = angular.isArray(defaults) ? defaults : [defaults]; - * - * // Append the new transformation to the defaults - * return defaults.concat(transform); - * } - * - * $http({ - * url: '...', - * method: 'GET', - * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { - * return doTransform(value); - * }) - * }); - * ``` - * - * - * ## Caching - * - * To enable caching, set the request configuration `cache` property to `true` (to use default - * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). - * When the cache is enabled, `$http` stores the response from the server in the specified - * cache. The next time the same request is made, the response is served from the cache without - * sending a request to the server. - * - * Note that even if the response is served from cache, delivery of the data is asynchronous in - * the same way that real requests are. - * - * If there are multiple GET requests for the same URL that should be cached using the same - * cache, but the cache is not populated yet, only one request to the server will be made and - * the remaining requests will be fulfilled using the response from the first request. - * - * You can change the default cache to a new object (built with - * {@link ng.$cacheFactory `$cacheFactory`}) by updating the - * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set - * their `cache` property to `true` will now use this cache object. - * - * If you set the default cache to `false` then only requests that specify their own custom - * cache object will be cached. - * - * ## Interceptors - * - * Before you start creating interceptors, be sure to understand the - * {@link ng.$q $q and deferred/promise APIs}. - * - * For purposes of global error handling, authentication, or any kind of synchronous or - * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be - * able to intercept requests before they are handed to the server and - * responses before they are handed over to the application code that - * initiated these requests. The interceptors leverage the {@link ng.$q - * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. - * - * The interceptors are service factories that are registered with the `$httpProvider` by - * adding them to the `$httpProvider.interceptors` array. The factory is called and - * injected with dependencies (if specified) and returns the interceptor. - * - * There are two kinds of interceptors (and two kinds of rejection interceptors): - * - * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to - * modify the `config` object or create a new one. The function needs to return the `config` - * object directly, or a promise containing the `config` or a new `config` object. - * * `requestError`: interceptor gets called when a previous interceptor threw an error or - * resolved with a rejection. - * * `response`: interceptors get called with http `response` object. The function is free to - * modify the `response` object or create a new one. The function needs to return the `response` - * object directly, or as a promise containing the `response` or a new `response` object. - * * `responseError`: interceptor gets called when a previous interceptor threw an error or - * resolved with a rejection. - * - * - * ```js - * // register the interceptor as a service - * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { - * return { - * // optional method - * 'request': function(config) { - * // do something on success - * return config; - * }, - * - * // optional method - * 'requestError': function(rejection) { - * // do something on error - * if (canRecover(rejection)) { - * return responseOrNewPromise - * } - * return $q.reject(rejection); - * }, - * - * - * - * // optional method - * 'response': function(response) { - * // do something on success - * return response; - * }, - * - * // optional method - * 'responseError': function(rejection) { - * // do something on error - * if (canRecover(rejection)) { - * return responseOrNewPromise - * } - * return $q.reject(rejection); - * } - * }; - * }); - * - * $httpProvider.interceptors.push('myHttpInterceptor'); - * - * - * // alternatively, register the interceptor via an anonymous factory - * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { - * return { - * 'request': function(config) { - * // same as above - * }, - * - * 'response': function(response) { - * // same as above - * } - * }; - * }); - * ``` - * - * ## Security Considerations - * - * When designing web applications, consider security threats from: - * - * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) - * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) - * - * Both server and the client must cooperate in order to eliminate these threats. Angular comes - * pre-configured with strategies that address these issues, but for this to work backend server - * cooperation is required. - * - * ### JSON Vulnerability Protection - * - * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) - * allows third party website to turn your JSON resource URL into - * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To - * counter this your server can prefix all JSON requests with following string `")]}',\n"`. - * Angular will automatically strip the prefix before processing it as JSON. - * - * For example if your server needs to return: - * ```js - * ['one','two'] - * ``` - * - * which is vulnerable to attack, your server can return: - * ```js - * )]}', - * ['one','two'] - * ``` - * - * Angular will strip the prefix, before processing the JSON. - * - * - * ### Cross Site Request Forgery (XSRF) Protection - * - * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which - * an unauthorized site can gain your user's private data. Angular provides a mechanism - * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie - * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only - * JavaScript that runs on your domain could read the cookie, your server can be assured that - * the XHR came from JavaScript running on your domain. The header will not be set for - * cross-domain requests. - * - * To take advantage of this, your server needs to set a token in a JavaScript readable session - * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the - * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure - * that only JavaScript running on your domain could have sent the request. The token must be - * unique for each user and must be verifiable by the server (to prevent the JavaScript from - * making up its own tokens). We recommend that the token is a digest of your site's - * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) - * for added security. - * - * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName - * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, - * or the per-request config object. - * - * In order to prevent collisions in environments where multiple Angular apps share the - * same domain or subdomain, we recommend that each application uses unique cookie name. - * - * @param {object} config Object describing the request to be made and how it should be - * processed. The object has following properties: - * - * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) - * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized - * with the `paramSerializer` and appended as GET parameters. - * - **data** – `{string|Object}` – Data to be sent as the request message data. - * - **headers** – `{Object}` – Map of strings or functions which return strings representing - * HTTP headers to send to the server. If the return value of a function is null, the - * header will not be sent. Functions accept a config object as an argument. - * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. - * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. - * - **transformRequest** – - * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – - * transform function or an array of such functions. The transform function takes the http - * request body and headers and returns its transformed (typically serialized) version. - * See {@link ng.$http#overriding-the-default-transformations-per-request - * Overriding the Default Transformations} - * - **transformResponse** – - * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` – - * transform function or an array of such functions. The transform function takes the http - * response body, headers and status and returns its transformed (typically deserialized) version. - * See {@link ng.$http#overriding-the-default-transformations-per-request - * Overriding the Default TransformationjqLiks} - * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to - * prepare the string representation of request parameters (specified as an object). - * If specified as string, it is interpreted as function registered with the - * {@link $injector $injector}, which means you can create your own serializer - * by registering it as a {@link auto.$provide#service service}. - * The default serializer is the {@link $httpParamSerializer $httpParamSerializer}; - * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike} - * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with - * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for - * caching. - * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} - * that should abort the request when resolved. - * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the - * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) - * for more information. - * - **responseType** - `{string}` - see - * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). - * - * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object - * when the request succeeds or fails. - * - * - * @property {Array.<Object>} pendingRequests Array of config objects for currently pending - * requests. This is primarily meant to be used for debugging purposes. - * - * - * @example -<example module="httpExample"> -<file name="index.html"> - <div ng-controller="FetchController"> - <select ng-model="method" aria-label="Request method"> - <option>GET</option> - <option>JSONP</option> - </select> - <input type="text" ng-model="url" size="80" aria-label="URL" /> - <button id="fetchbtn" ng-click="fetch()">fetch</button><br> - <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> - <button id="samplejsonpbtn" - ng-click="updateModel('JSONP', - 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')"> - Sample JSONP - </button> - <button id="invalidjsonpbtn" - ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')"> - Invalid JSONP - </button> - <pre>http status code: {{status}}</pre> - <pre>http response data: {{data}}</pre> - </div> -</file> -<file name="script.js"> - angular.module('httpExample', []) - .controller('FetchController', ['$scope', '$http', '$templateCache', - function($scope, $http, $templateCache) { - $scope.method = 'GET'; - $scope.url = 'http-hello.html'; - - $scope.fetch = function() { - $scope.code = null; - $scope.response = null; - - $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - then(function(response) { - $scope.status = response.status; - $scope.data = response.data; - }, function(response) { - $scope.data = response.data || "Request failed"; - $scope.status = response.status; - }); - }; - - $scope.updateModel = function(method, url) { - $scope.method = method; - $scope.url = url; - }; - }]); -</file> -<file name="http-hello.html"> - Hello, $http! -</file> -<file name="protractor.js" type="protractor"> - var status = element(by.binding('status')); - var data = element(by.binding('data')); - var fetchBtn = element(by.id('fetchbtn')); - var sampleGetBtn = element(by.id('samplegetbtn')); - var sampleJsonpBtn = element(by.id('samplejsonpbtn')); - var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); - - it('should make an xhr GET request', function() { - sampleGetBtn.click(); - fetchBtn.click(); - expect(status.getText()).toMatch('200'); - expect(data.getText()).toMatch(/Hello, \$http!/); - }); - -// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 -// it('should make a JSONP request to angularjs.org', function() { -// sampleJsonpBtn.click(); -// fetchBtn.click(); -// expect(status.getText()).toMatch('200'); -// expect(data.getText()).toMatch(/Super Hero!/); -// }); - - it('should make JSONP request to invalid URL and invoke the error handler', - function() { - invalidJsonpBtn.click(); - fetchBtn.click(); - expect(status.getText()).toMatch('0'); - expect(data.getText()).toMatch('Request failed'); - }); -</file> -</example> - */ - function $http(requestConfig) { - - if (!angular.isObject(requestConfig)) { - throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); - } - - var config = extend({ - method: 'get', - transformRequest: defaults.transformRequest, - transformResponse: defaults.transformResponse, - paramSerializer: defaults.paramSerializer - }, requestConfig); - - config.headers = mergeHeaders(requestConfig); - config.method = uppercase(config.method); - config.paramSerializer = isString(config.paramSerializer) ? - $injector.get(config.paramSerializer) : config.paramSerializer; - - var serverRequest = function(config) { - var headers = config.headers; - var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); - - // strip content-type if data is undefined - if (isUndefined(reqData)) { - forEach(headers, function(value, header) { - if (lowercase(header) === 'content-type') { - delete headers[header]; - } - }); - } - - if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { - config.withCredentials = defaults.withCredentials; - } - - // send request - return sendReq(config, reqData).then(transformResponse, transformResponse); - }; - - var chain = [serverRequest, undefined]; - var promise = $q.when(config); - - // apply interceptors - forEach(reversedInterceptors, function(interceptor) { - if (interceptor.request || interceptor.requestError) { - chain.unshift(interceptor.request, interceptor.requestError); - } - if (interceptor.response || interceptor.responseError) { - chain.push(interceptor.response, interceptor.responseError); - } - }); - - while (chain.length) { - var thenFn = chain.shift(); - var rejectFn = chain.shift(); - - promise = promise.then(thenFn, rejectFn); - } - - if (useLegacyPromise) { - promise.success = function(fn) { - assertArgFn(fn, 'fn'); - - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - promise.error = function(fn) { - assertArgFn(fn, 'fn'); - - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - } else { - promise.success = $httpMinErrLegacyFn('success'); - promise.error = $httpMinErrLegacyFn('error'); - } - - return promise; - - function transformResponse(response) { - // make a copy since the response must be cacheable - var resp = extend({}, response); - if (!response.data) { - resp.data = response.data; - } else { - resp.data = transformData(response.data, response.headers, response.status, config.transformResponse); - } - return (isSuccess(response.status)) - ? resp - : $q.reject(resp); - } - - function executeHeaderFns(headers, config) { - var headerContent, processedHeaders = {}; - - forEach(headers, function(headerFn, header) { - if (isFunction(headerFn)) { - headerContent = headerFn(config); - if (headerContent != null) { - processedHeaders[header] = headerContent; - } - } else { - processedHeaders[header] = headerFn; - } - }); - - return processedHeaders; - } - - function mergeHeaders(config) { - var defHeaders = defaults.headers, - reqHeaders = extend({}, config.headers), - defHeaderName, lowercaseDefHeaderName, reqHeaderName; - - defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); - - // using for-in instead of forEach to avoid unecessary iteration after header has been found - defaultHeadersIteration: - for (defHeaderName in defHeaders) { - lowercaseDefHeaderName = lowercase(defHeaderName); - - for (reqHeaderName in reqHeaders) { - if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { - continue defaultHeadersIteration; - } - } - - reqHeaders[defHeaderName] = defHeaders[defHeaderName]; - } - - // execute if header value is a function for merged headers - return executeHeaderFns(reqHeaders, shallowCopy(config)); - } - } - - $http.pendingRequests = []; - - /** - * @ngdoc method - * @name $http#get - * - * @description - * Shortcut method to perform `GET` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#delete - * - * @description - * Shortcut method to perform `DELETE` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#head - * - * @description - * Shortcut method to perform `HEAD` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#jsonp - * - * @description - * Shortcut method to perform `JSONP` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request. - * The name of the callback should be the string `JSON_CALLBACK`. - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethods('get', 'delete', 'head', 'jsonp'); - - /** - * @ngdoc method - * @name $http#post - * - * @description - * Shortcut method to perform `POST` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#put - * - * @description - * Shortcut method to perform `PUT` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name $http#patch - * - * @description - * Shortcut method to perform `PATCH` request. - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethodsWithData('post', 'put', 'patch'); - - /** - * @ngdoc property - * @name $http#defaults - * - * @description - * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of - * default headers, withCredentials as well as request and response transformations. - * - * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. - */ - $http.defaults = defaults; - - - return $http; - - - function createShortMethods(names) { - forEach(arguments, function(name) { - $http[name] = function(url, config) { - return $http(extend({}, config || {}, { - method: name, - url: url - })); - }; - }); - } - - - function createShortMethodsWithData(name) { - forEach(arguments, function(name) { - $http[name] = function(url, data, config) { - return $http(extend({}, config || {}, { - method: name, - url: url, - data: data - })); - }; - }); - } - - - /** - * Makes the request. - * - * !!! ACCESSES CLOSURE VARS: - * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests - */ - function sendReq(config, reqData) { - var deferred = $q.defer(), - promise = deferred.promise, - cache, - cachedResp, - reqHeaders = config.headers, - url = buildUrl(config.url, config.paramSerializer(config.params)); - - $http.pendingRequests.push(config); - promise.then(removePendingReq, removePendingReq); - - - if ((config.cache || defaults.cache) && config.cache !== false && - (config.method === 'GET' || config.method === 'JSONP')) { - cache = isObject(config.cache) ? config.cache - : isObject(defaults.cache) ? defaults.cache - : defaultCache; - } - - if (cache) { - cachedResp = cache.get(url); - if (isDefined(cachedResp)) { - if (isPromiseLike(cachedResp)) { - // cached request has already been sent, but there is no response yet - cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); - } else { - // serving from cache - if (isArray(cachedResp)) { - resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); - } else { - resolvePromise(cachedResp, 200, {}, 'OK'); - } - } - } else { - // put the promise for the non-transformed response into cache as a placeholder - cache.put(url, promise); - } - } - - - // if we won't have the response in cache, set the xsrf headers and - // send the request to the backend - if (isUndefined(cachedResp)) { - var xsrfValue = urlIsSameOrigin(config.url) - ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName] - : undefined; - if (xsrfValue) { - reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; - } - - $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, - config.withCredentials, config.responseType); - } - - return promise; - - - /** - * Callback registered to $httpBackend(): - * - caches the response if desired - * - resolves the raw $http promise - * - calls $apply - */ - function done(status, response, headersString, statusText) { - if (cache) { - if (isSuccess(status)) { - cache.put(url, [status, response, parseHeaders(headersString), statusText]); - } else { - // remove promise from the cache - cache.remove(url); - } - } - - function resolveHttpPromise() { - resolvePromise(response, status, headersString, statusText); - } - - if (useApplyAsync) { - $rootScope.$applyAsync(resolveHttpPromise); - } else { - resolveHttpPromise(); - if (!$rootScope.$$phase) $rootScope.$apply(); - } - } - - - /** - * Resolves the raw $http promise. - */ - function resolvePromise(response, status, headers, statusText) { - //status: HTTP response status code, 0, -1 (aborted by timeout / promise) - status = status >= -1 ? status : 0; - - (isSuccess(status) ? deferred.resolve : deferred.reject)({ - data: response, - status: status, - headers: headersGetter(headers), - config: config, - statusText: statusText - }); - } - - function resolvePromiseWithResult(result) { - resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); - } - - function removePendingReq() { - var idx = $http.pendingRequests.indexOf(config); - if (idx !== -1) $http.pendingRequests.splice(idx, 1); - } - } - - - function buildUrl(url, serializedParams) { - if (serializedParams.length > 0) { - url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams; - } - return url; - } - }]; -} - -/** - * @ngdoc service - * @name $xhrFactory - * - * @description - * Factory function used to create XMLHttpRequest objects. - * - * Replace or decorate this service to create your own custom XMLHttpRequest objects. - * - * ``` - * angular.module('myApp', []) - * .factory('$xhrFactory', function() { - * return function createXhr(method, url) { - * return new window.XMLHttpRequest({mozSystem: true}); - * }; - * }); - * ``` - * - * @param {string} method HTTP method of the request (GET, POST, PUT, ..) - * @param {string} url URL of the request. - */ -function $xhrFactoryProvider() { - this.$get = function() { - return function createXhr() { - return new window.XMLHttpRequest(); - }; - }; -} - -/** - * @ngdoc service - * @name $httpBackend - * @requires $window - * @requires $document - * @requires $xhrFactory - * - * @description - * HTTP backend used by the {@link ng.$http service} that delegates to - * XMLHttpRequest object or JSONP and deals with browser incompatibilities. - * - * You should never need to use this service directly, instead use the higher-level abstractions: - * {@link ng.$http $http} or {@link ngResource.$resource $resource}. - * - * During testing this implementation is swapped with {@link ngMock.$httpBackend mock - * $httpBackend} which can be trained with responses. - */ -function $HttpBackendProvider() { - this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) { - return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]); - }]; -} - -function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { - // TODO(vojta): fix the signature - return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { - $browser.$$incOutstandingRequestCount(); - url = url || $browser.url(); - - if (lowercase(method) == 'jsonp') { - var callbackId = '_' + (callbacks.counter++).toString(36); - callbacks[callbackId] = function(data) { - callbacks[callbackId].data = data; - callbacks[callbackId].called = true; - }; - - var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), - callbackId, function(status, text) { - completeRequest(callback, status, callbacks[callbackId].data, "", text); - callbacks[callbackId] = noop; - }); - } else { - - var xhr = createXhr(method, url); - - xhr.open(method, url, true); - forEach(headers, function(value, key) { - if (isDefined(value)) { - xhr.setRequestHeader(key, value); - } - }); - - xhr.onload = function requestLoaded() { - var statusText = xhr.statusText || ''; - - // responseText is the old-school way of retrieving response (supported by IE9) - // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) - var response = ('response' in xhr) ? xhr.response : xhr.responseText; - - // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) - var status = xhr.status === 1223 ? 204 : xhr.status; - - // fix status code when it is 0 (0 status is undocumented). - // Occurs when accessing file resources or on Android 4.1 stock browser - // while retrieving files from application cache. - if (status === 0) { - status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; - } - - completeRequest(callback, - status, - response, - xhr.getAllResponseHeaders(), - statusText); - }; - - var requestError = function() { - // The response is always empty - // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error - completeRequest(callback, -1, null, null, ''); - }; - - xhr.onerror = requestError; - xhr.onabort = requestError; - - if (withCredentials) { - xhr.withCredentials = true; - } - - if (responseType) { - try { - xhr.responseType = responseType; - } catch (e) { - // WebKit added support for the json responseType value on 09/03/2013 - // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are - // known to throw when setting the value "json" as the response type. Other older - // browsers implementing the responseType - // - // The json response type can be ignored if not supported, because JSON payloads are - // parsed on the client-side regardless. - if (responseType !== 'json') { - throw e; - } - } - } - - xhr.send(isUndefined(post) ? null : post); - } - - if (timeout > 0) { - var timeoutId = $browserDefer(timeoutRequest, timeout); - } else if (isPromiseLike(timeout)) { - timeout.then(timeoutRequest); - } - - - function timeoutRequest() { - jsonpDone && jsonpDone(); - xhr && xhr.abort(); - } - - function completeRequest(callback, status, response, headersString, statusText) { - // cancel timeout and subsequent timeout promise resolution - if (isDefined(timeoutId)) { - $browserDefer.cancel(timeoutId); - } - jsonpDone = xhr = null; - - callback(status, response, headersString, statusText); - $browser.$$completeOutstandingRequest(noop); - } - }; - - function jsonpReq(url, callbackId, done) { - // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.: - // - fetches local scripts via XHR and evals them - // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'), callback = null; - script.type = "text/javascript"; - script.src = url; - script.async = true; - - callback = function(event) { - removeEventListenerFn(script, "load", callback); - removeEventListenerFn(script, "error", callback); - rawDocument.body.removeChild(script); - script = null; - var status = -1; - var text = "unknown"; - - if (event) { - if (event.type === "load" && !callbacks[callbackId].called) { - event = { type: "error" }; - } - text = event.type; - status = event.type === "error" ? 404 : 200; - } - - if (done) { - done(status, text); - } - }; - - addEventListenerFn(script, "load", callback); - addEventListenerFn(script, "error", callback); - rawDocument.body.appendChild(script); - return callback; - } -} - -var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate'); -$interpolateMinErr.throwNoconcat = function(text) { - throw $interpolateMinErr('noconcat', - "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + - "interpolations that concatenate multiple expressions when a trusted value is " + - "required. See http://docs.angularjs.org/api/ng.$sce", text); -}; - -$interpolateMinErr.interr = function(text, err) { - return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); -}; - -/** - * @ngdoc provider - * @name $interpolateProvider - * - * @description - * - * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. - * - * @example -<example module="customInterpolationApp"> -<file name="index.html"> -<script> - var customInterpolationApp = angular.module('customInterpolationApp', []); - - customInterpolationApp.config(function($interpolateProvider) { - $interpolateProvider.startSymbol('//'); - $interpolateProvider.endSymbol('//'); - }); - - - customInterpolationApp.controller('DemoController', function() { - this.label = "This binding is brought you by // interpolation symbols."; - }); -</script> -<div ng-app="App" ng-controller="DemoController as demo"> - //demo.label// -</div> -</file> -<file name="protractor.js" type="protractor"> - it('should interpolate binding with custom symbols', function() { - expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); - }); -</file> -</example> - */ -function $InterpolateProvider() { - var startSymbol = '{{'; - var endSymbol = '}}'; - - /** - * @ngdoc method - * @name $interpolateProvider#startSymbol - * @description - * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. - * - * @param {string=} value new value to set the starting symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.startSymbol = function(value) { - if (value) { - startSymbol = value; - return this; - } else { - return startSymbol; - } - }; - - /** - * @ngdoc method - * @name $interpolateProvider#endSymbol - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * @param {string=} value new value to set the ending symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.endSymbol = function(value) { - if (value) { - endSymbol = value; - return this; - } else { - return endSymbol; - } - }; - - - this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { - var startSymbolLength = startSymbol.length, - endSymbolLength = endSymbol.length, - escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), - escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); - - function escape(ch) { - return '\\\\\\' + ch; - } - - function unescapeText(text) { - return text.replace(escapedStartRegexp, startSymbol). - replace(escapedEndRegexp, endSymbol); - } - - function stringify(value) { - if (value == null) { // null || undefined - return ''; - } - switch (typeof value) { - case 'string': - break; - case 'number': - value = '' + value; - break; - default: - value = toJson(value); - } - - return value; - } - - /** - * @ngdoc service - * @name $interpolate - * @kind function - * - * @requires $parse - * @requires $sce - * - * @description - * - * Compiles a string with markup into an interpolation function. This service is used by the - * HTML {@link ng.$compile $compile} service for data binding. See - * {@link ng.$interpolateProvider $interpolateProvider} for configuring the - * interpolation markup. - * - * - * ```js - * var $interpolate = ...; // injected - * var exp = $interpolate('Hello {{name | uppercase}}!'); - * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!'); - * ``` - * - * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is - * `true`, the interpolation function will return `undefined` unless all embedded expressions - * evaluate to a value other than `undefined`. - * - * ```js - * var $interpolate = ...; // injected - * var context = {greeting: 'Hello', name: undefined }; - * - * // default "forgiving" mode - * var exp = $interpolate('{{greeting}} {{name}}!'); - * expect(exp(context)).toEqual('Hello !'); - * - * // "allOrNothing" mode - * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); - * expect(exp(context)).toBeUndefined(); - * context.name = 'Angular'; - * expect(exp(context)).toEqual('Hello Angular!'); - * ``` - * - * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. - * - * ####Escaped Interpolation - * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers - * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). - * It will be rendered as a regular start/end marker, and will not be interpreted as an expression - * or binding. - * - * This enables web-servers to prevent script injection attacks and defacing attacks, to some - * degree, while also enabling code examples to work without relying on the - * {@link ng.directive:ngNonBindable ngNonBindable} directive. - * - * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, - * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all - * interpolation start/end markers with their escaped counterparts.** - * - * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered - * output when the $interpolate service processes the text. So, for HTML elements interpolated - * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter - * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, - * this is typically useful only when user-data is used in rendering a template from the server, or - * when otherwise untrusted data is used by a directive. - * - * <example> - * <file name="index.html"> - * <div ng-init="username='A user'"> - * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\} - * </p> - * <p><strong>{{username}}</strong> attempts to inject code which will deface the - * application, but fails to accomplish their task, because the server has correctly - * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) - * characters.</p> - * <p>Instead, the result of the attempted script injection is visible, and can be removed - * from the database by an administrator.</p> - * </div> - * </file> - * </example> - * - * @param {string} text The text with markup to interpolate. - * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have - * embedded expression in order to return an interpolation function. Strings with no - * embedded expression will return null for the interpolation function. - * @param {string=} trustedContext when provided, the returned function passes the interpolated - * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, - * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that - * provides Strict Contextual Escaping for details. - * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined - * unless all embedded expressions evaluate to a value other than `undefined`. - * @returns {function(context)} an interpolation function which is used to compute the - * interpolated string. The function has these parameters: - * - * - `context`: evaluation context for all expressions embedded in the interpolated text - */ - function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { - allOrNothing = !!allOrNothing; - var startIndex, - endIndex, - index = 0, - expressions = [], - parseFns = [], - textLength = text.length, - exp, - concat = [], - expressionPositions = []; - - while (index < textLength) { - if (((startIndex = text.indexOf(startSymbol, index)) != -1) && - ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) { - if (index !== startIndex) { - concat.push(unescapeText(text.substring(index, startIndex))); - } - exp = text.substring(startIndex + startSymbolLength, endIndex); - expressions.push(exp); - parseFns.push($parse(exp, parseStringifyInterceptor)); - index = endIndex + endSymbolLength; - expressionPositions.push(concat.length); - concat.push(''); - } else { - // we did not find an interpolation, so we have to add the remainder to the separators array - if (index !== textLength) { - concat.push(unescapeText(text.substring(index))); - } - break; - } - } - - // Concatenating expressions makes it hard to reason about whether some combination of - // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a - // single expression be used for iframe[src], object[src], etc., we ensure that the value - // that's used is assigned or constructed by some JS code somewhere that is more testable or - // make it obvious that you bound the value to some user controlled value. This helps reduce - // the load when auditing for XSS issues. - if (trustedContext && concat.length > 1) { - $interpolateMinErr.throwNoconcat(text); - } - - if (!mustHaveExpression || expressions.length) { - var compute = function(values) { - for (var i = 0, ii = expressions.length; i < ii; i++) { - if (allOrNothing && isUndefined(values[i])) return; - concat[expressionPositions[i]] = values[i]; - } - return concat.join(''); - }; - - var getValue = function(value) { - return trustedContext ? - $sce.getTrusted(trustedContext, value) : - $sce.valueOf(value); - }; - - return extend(function interpolationFn(context) { - var i = 0; - var ii = expressions.length; - var values = new Array(ii); - - try { - for (; i < ii; i++) { - values[i] = parseFns[i](context); - } - - return compute(values); - } catch (err) { - $exceptionHandler($interpolateMinErr.interr(text, err)); - } - - }, { - // all of these properties are undocumented for now - exp: text, //just for compatibility with regular watchers created via $watch - expressions: expressions, - $$watchDelegate: function(scope, listener) { - var lastValue; - return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { - var currValue = compute(values); - if (isFunction(listener)) { - listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); - } - lastValue = currValue; - }); - } - }); - } - - function parseStringifyInterceptor(value) { - try { - value = getValue(value); - return allOrNothing && !isDefined(value) ? value : stringify(value); - } catch (err) { - $exceptionHandler($interpolateMinErr.interr(text, err)); - } - } - } - - - /** - * @ngdoc method - * @name $interpolate#startSymbol - * @description - * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. - * - * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change - * the symbol. - * - * @returns {string} start symbol. - */ - $interpolate.startSymbol = function() { - return startSymbol; - }; - - - /** - * @ngdoc method - * @name $interpolate#endSymbol - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change - * the symbol. - * - * @returns {string} end symbol. - */ - $interpolate.endSymbol = function() { - return endSymbol; - }; - - return $interpolate; - }]; -} - -function $IntervalProvider() { - this.$get = ['$rootScope', '$window', '$q', '$$q', - function($rootScope, $window, $q, $$q) { - var intervals = {}; - - - /** - * @ngdoc service - * @name $interval - * - * @description - * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` - * milliseconds. - * - * The return value of registering an interval function is a promise. This promise will be - * notified upon each tick of the interval, and will be resolved after `count` iterations, or - * run indefinitely if `count` is not defined. The value of the notification will be the - * number of iterations that have run. - * To cancel an interval, call `$interval.cancel(promise)`. - * - * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to - * move forward by `millis` milliseconds and trigger any functions scheduled to run in that - * time. - * - * <div class="alert alert-warning"> - * **Note**: Intervals created by this service must be explicitly destroyed when you are finished - * with them. In particular they are not automatically destroyed when a controller's scope or a - * directive's element are destroyed. - * You should take this into consideration and make sure to always cancel the interval at the - * appropriate moment. See the example below for more details on how and when to do this. - * </div> - * - * @param {function()} fn A function that should be called repeatedly. - * @param {number} delay Number of milliseconds between each function call. - * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat - * indefinitely. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @param {...*=} Pass additional parameters to the executed function. - * @returns {promise} A promise which will be notified on each iteration. - * - * @example - * <example module="intervalExample"> - * <file name="index.html"> - * <script> - * angular.module('intervalExample', []) - * .controller('ExampleController', ['$scope', '$interval', - * function($scope, $interval) { - * $scope.format = 'M/d/yy h:mm:ss a'; - * $scope.blood_1 = 100; - * $scope.blood_2 = 120; - * - * var stop; - * $scope.fight = function() { - * // Don't start a new fight if we are already fighting - * if ( angular.isDefined(stop) ) return; - * - * stop = $interval(function() { - * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { - * $scope.blood_1 = $scope.blood_1 - 3; - * $scope.blood_2 = $scope.blood_2 - 4; - * } else { - * $scope.stopFight(); - * } - * }, 100); - * }; - * - * $scope.stopFight = function() { - * if (angular.isDefined(stop)) { - * $interval.cancel(stop); - * stop = undefined; - * } - * }; - * - * $scope.resetFight = function() { - * $scope.blood_1 = 100; - * $scope.blood_2 = 120; - * }; - * - * $scope.$on('$destroy', function() { - * // Make sure that the interval is destroyed too - * $scope.stopFight(); - * }); - * }]) - * // Register the 'myCurrentTime' directive factory method. - * // We inject $interval and dateFilter service since the factory method is DI. - * .directive('myCurrentTime', ['$interval', 'dateFilter', - * function($interval, dateFilter) { - * // return the directive link function. (compile function not needed) - * return function(scope, element, attrs) { - * var format, // date format - * stopTime; // so that we can cancel the time updates - * - * // used to update the UI - * function updateTime() { - * element.text(dateFilter(new Date(), format)); - * } - * - * // watch the expression, and update the UI on change. - * scope.$watch(attrs.myCurrentTime, function(value) { - * format = value; - * updateTime(); - * }); - * - * stopTime = $interval(updateTime, 1000); - * - * // listen on DOM destroy (removal) event, and cancel the next UI update - * // to prevent updating time after the DOM element was removed. - * element.on('$destroy', function() { - * $interval.cancel(stopTime); - * }); - * } - * }]); - * </script> - * - * <div> - * <div ng-controller="ExampleController"> - * <label>Date format: <input ng-model="format"></label> <hr/> - * Current time is: <span my-current-time="format"></span> - * <hr/> - * Blood 1 : <font color='red'>{{blood_1}}</font> - * Blood 2 : <font color='red'>{{blood_2}}</font> - * <button type="button" data-ng-click="fight()">Fight</button> - * <button type="button" data-ng-click="stopFight()">StopFight</button> - * <button type="button" data-ng-click="resetFight()">resetFight</button> - * </div> - * </div> - * - * </file> - * </example> - */ - function interval(fn, delay, count, invokeApply) { - var hasParams = arguments.length > 4, - args = hasParams ? sliceArgs(arguments, 4) : [], - setInterval = $window.setInterval, - clearInterval = $window.clearInterval, - iteration = 0, - skipApply = (isDefined(invokeApply) && !invokeApply), - deferred = (skipApply ? $$q : $q).defer(), - promise = deferred.promise; - - count = isDefined(count) ? count : 0; - - promise.then(null, null, (!hasParams) ? fn : function() { - fn.apply(null, args); - }); - - promise.$$intervalId = setInterval(function tick() { - deferred.notify(iteration++); - - if (count > 0 && iteration >= count) { - deferred.resolve(iteration); - clearInterval(promise.$$intervalId); - delete intervals[promise.$$intervalId]; - } - - if (!skipApply) $rootScope.$apply(); - - }, delay); - - intervals[promise.$$intervalId] = deferred; - - return promise; - } - - - /** - * @ngdoc method - * @name $interval#cancel - * - * @description - * Cancels a task associated with the `promise`. - * - * @param {Promise=} promise returned by the `$interval` function. - * @returns {boolean} Returns `true` if the task was successfully canceled. - */ - interval.cancel = function(promise) { - if (promise && promise.$$intervalId in intervals) { - intervals[promise.$$intervalId].reject('canceled'); - $window.clearInterval(promise.$$intervalId); - delete intervals[promise.$$intervalId]; - return true; - } - return false; - }; - - return interval; - }]; -} - -/** - * @ngdoc service - * @name $locale - * - * @description - * $locale service provides localization rules for various Angular components. As of right now the - * only public api is: - * - * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) - */ - -var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, - DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; -var $locationMinErr = minErr('$location'); - - -/** - * Encode path using encodeUriSegment, ignoring forward slashes - * - * @param {string} path Path to encode - * @returns {string} - */ -function encodePath(path) { - var segments = path.split('/'), - i = segments.length; - - while (i--) { - segments[i] = encodeUriSegment(segments[i]); - } - - return segments.join('/'); -} - -function parseAbsoluteUrl(absoluteUrl, locationObj) { - var parsedUrl = urlResolve(absoluteUrl); - - locationObj.$$protocol = parsedUrl.protocol; - locationObj.$$host = parsedUrl.hostname; - locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; -} - - -function parseAppUrl(relativeUrl, locationObj) { - var prefixed = (relativeUrl.charAt(0) !== '/'); - if (prefixed) { - relativeUrl = '/' + relativeUrl; - } - var match = urlResolve(relativeUrl); - locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? - match.pathname.substring(1) : match.pathname); - locationObj.$$search = parseKeyValue(match.search); - locationObj.$$hash = decodeURIComponent(match.hash); - - // make sure path starts with '/'; - if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { - locationObj.$$path = '/' + locationObj.$$path; - } -} - - -/** - * - * @param {string} begin - * @param {string} whole - * @returns {string} returns text from whole after begin or undefined if it does not begin with - * expected string. - */ -function beginsWith(begin, whole) { - if (whole.indexOf(begin) === 0) { - return whole.substr(begin.length); - } -} - - -function stripHash(url) { - var index = url.indexOf('#'); - return index == -1 ? url : url.substr(0, index); -} - -function trimEmptyHash(url) { - return url.replace(/(#.+)|#$/, '$1'); -} - - -function stripFile(url) { - return url.substr(0, stripHash(url).lastIndexOf('/') + 1); -} - -/* return the server only (scheme://host:port) */ -function serverBase(url) { - return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); -} - - -/** - * LocationHtml5Url represents an url - * This object is exposed as $location service when HTML5 mode is enabled and supported - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} basePrefix url path prefix - */ -function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { - this.$$html5 = true; - basePrefix = basePrefix || ''; - parseAbsoluteUrl(appBase, this); - - - /** - * Parse given html5 (regular) url string into properties - * @param {string} url HTML5 url - * @private - */ - this.$$parse = function(url) { - var pathUrl = beginsWith(appBaseNoFile, url); - if (!isString(pathUrl)) { - throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, - appBaseNoFile); - } - - parseAppUrl(pathUrl, this); - - if (!this.$$path) { - this.$$path = '/'; - } - - this.$$compose(); - }; - - /** - * Compose url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' - }; - - this.$$parseLinkUrl = function(url, relHref) { - if (relHref && relHref[0] === '#') { - // special case for links to hash fragments: - // keep the old url and only replace the hash fragment - this.hash(relHref.slice(1)); - return true; - } - var appUrl, prevAppUrl; - var rewrittenUrl; - - if (isDefined(appUrl = beginsWith(appBase, url))) { - prevAppUrl = appUrl; - if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) { - rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); - } else { - rewrittenUrl = appBase + prevAppUrl; - } - } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) { - rewrittenUrl = appBaseNoFile + appUrl; - } else if (appBaseNoFile == url + '/') { - rewrittenUrl = appBaseNoFile; - } - if (rewrittenUrl) { - this.$$parse(rewrittenUrl); - } - return !!rewrittenUrl; - }; -} - - -/** - * LocationHashbangUrl represents url - * This object is exposed as $location service when developer doesn't opt into html5 mode. - * It also serves as the base class for html5 mode fallback on legacy browsers. - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} hashPrefix hashbang prefix - */ -function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { - - parseAbsoluteUrl(appBase, this); - - - /** - * Parse given hashbang url into properties - * @param {string} url Hashbang url - * @private - */ - this.$$parse = function(url) { - var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); - var withoutHashUrl; - - if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') { - - // The rest of the url starts with a hash so we have - // got either a hashbang path or a plain hash fragment - withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl); - if (isUndefined(withoutHashUrl)) { - // There was no hashbang prefix so we just have a hash fragment - withoutHashUrl = withoutBaseUrl; - } - - } else { - // There was no hashbang path nor hash fragment: - // If we are in HTML5 mode we use what is left as the path; - // Otherwise we ignore what is left - if (this.$$html5) { - withoutHashUrl = withoutBaseUrl; - } else { - withoutHashUrl = ''; - if (isUndefined(withoutBaseUrl)) { - appBase = url; - this.replace(); - } - } - } - - parseAppUrl(withoutHashUrl, this); - - this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); - - this.$$compose(); - - /* - * In Windows, on an anchor node on documents loaded from - * the filesystem, the browser will return a pathname - * prefixed with the drive name ('/C:/path') when a - * pathname without a drive is set: - * * a.setAttribute('href', '/foo') - * * a.pathname === '/C:/foo' //true - * - * Inside of Angular, we're always using pathnames that - * do not include drive names for routing. - */ - function removeWindowsDriveName(path, url, base) { - /* - Matches paths for file protocol on windows, - such as /C:/foo/bar, and captures only /foo/bar. - */ - var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; - - var firstPathSegmentMatch; - - //Get the relative path from the input URL. - if (url.indexOf(base) === 0) { - url = url.replace(base, ''); - } - - // The input URL intentionally contains a first path segment that ends with a colon. - if (windowsFilePathExp.exec(url)) { - return path; - } - - firstPathSegmentMatch = windowsFilePathExp.exec(path); - return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; - } - }; - - /** - * Compose hashbang url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); - }; - - this.$$parseLinkUrl = function(url, relHref) { - if (stripHash(appBase) == stripHash(url)) { - this.$$parse(url); - return true; - } - return false; - }; -} - - -/** - * LocationHashbangUrl represents url - * This object is exposed as $location service when html5 history api is enabled but the browser - * does not support it. - * - * @constructor - * @param {string} appBase application base URL - * @param {string} appBaseNoFile application base URL stripped of any filename - * @param {string} hashPrefix hashbang prefix - */ -function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { - this.$$html5 = true; - LocationHashbangUrl.apply(this, arguments); - - this.$$parseLinkUrl = function(url, relHref) { - if (relHref && relHref[0] === '#') { - // special case for links to hash fragments: - // keep the old url and only replace the hash fragment - this.hash(relHref.slice(1)); - return true; - } - - var rewrittenUrl; - var appUrl; - - if (appBase == stripHash(url)) { - rewrittenUrl = url; - } else if ((appUrl = beginsWith(appBaseNoFile, url))) { - rewrittenUrl = appBase + hashPrefix + appUrl; - } else if (appBaseNoFile === url + '/') { - rewrittenUrl = appBaseNoFile; - } - if (rewrittenUrl) { - this.$$parse(rewrittenUrl); - } - return !!rewrittenUrl; - }; - - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' - this.$$absUrl = appBase + hashPrefix + this.$$url; - }; - -} - - -var locationPrototype = { - - /** - * Are we in html5 mode? - * @private - */ - $$html5: false, - - /** - * Has any change been replacing? - * @private - */ - $$replace: false, - - /** - * @ngdoc method - * @name $location#absUrl - * - * @description - * This method is getter only. - * - * Return full url representation with all segments encoded according to rules specified in - * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var absUrl = $location.absUrl(); - * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" - * ``` - * - * @return {string} full url - */ - absUrl: locationGetter('$$absUrl'), - - /** - * @ngdoc method - * @name $location#url - * - * @description - * This method is getter / setter. - * - * Return url (e.g. `/path?a=b#hash`) when called without any parameter. - * - * Change path, search and hash, when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var url = $location.url(); - * // => "/some/path?foo=bar&baz=xoxo" - * ``` - * - * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) - * @return {string} url - */ - url: function(url) { - if (isUndefined(url)) { - return this.$$url; - } - - var match = PATH_MATCH.exec(url); - if (match[1] || url === '') this.path(decodeURIComponent(match[1])); - if (match[2] || match[1] || url === '') this.search(match[3] || ''); - this.hash(match[5] || ''); - - return this; - }, - - /** - * @ngdoc method - * @name $location#protocol - * - * @description - * This method is getter only. - * - * Return protocol of current url. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var protocol = $location.protocol(); - * // => "http" - * ``` - * - * @return {string} protocol of current url - */ - protocol: locationGetter('$$protocol'), - - /** - * @ngdoc method - * @name $location#host - * - * @description - * This method is getter only. - * - * Return host of current url. - * - * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var host = $location.host(); - * // => "example.com" - * - * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo - * host = $location.host(); - * // => "example.com" - * host = location.host; - * // => "example.com:8080" - * ``` - * - * @return {string} host of current url. - */ - host: locationGetter('$$host'), - - /** - * @ngdoc method - * @name $location#port - * - * @description - * This method is getter only. - * - * Return port of current url. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var port = $location.port(); - * // => 80 - * ``` - * - * @return {Number} port - */ - port: locationGetter('$$port'), - - /** - * @ngdoc method - * @name $location#path - * - * @description - * This method is getter / setter. - * - * Return path of current url when called without any parameter. - * - * Change path when called with parameter and return `$location`. - * - * Note: Path should always begin with forward slash (/), this method will add the forward slash - * if it is missing. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var path = $location.path(); - * // => "/some/path" - * ``` - * - * @param {(string|number)=} path New path - * @return {string} path - */ - path: locationGetterSetter('$$path', function(path) { - path = path !== null ? path.toString() : ''; - return path.charAt(0) == '/' ? path : '/' + path; - }), - - /** - * @ngdoc method - * @name $location#search - * - * @description - * This method is getter / setter. - * - * Return search part (as object) of current url when called without any parameter. - * - * Change search part when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo - * var searchObject = $location.search(); - * // => {foo: 'bar', baz: 'xoxo'} - * - * // set foo to 'yipee' - * $location.search('foo', 'yipee'); - * // $location.search() => {foo: 'yipee', baz: 'xoxo'} - * ``` - * - * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or - * hash object. - * - * When called with a single argument the method acts as a setter, setting the `search` component - * of `$location` to the specified value. - * - * If the argument is a hash object containing an array of values, these values will be encoded - * as duplicate search parameters in the url. - * - * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` - * will override only a single search property. - * - * If `paramValue` is an array, it will override the property of the `search` component of - * `$location` specified via the first argument. - * - * If `paramValue` is `null`, the property specified via the first argument will be deleted. - * - * If `paramValue` is `true`, the property specified via the first argument will be added with no - * value nor trailing equal sign. - * - * @return {Object} If called with no arguments returns the parsed `search` object. If called with - * one or more arguments returns `$location` object itself. - */ - search: function(search, paramValue) { - switch (arguments.length) { - case 0: - return this.$$search; - case 1: - if (isString(search) || isNumber(search)) { - search = search.toString(); - this.$$search = parseKeyValue(search); - } else if (isObject(search)) { - search = copy(search, {}); - // remove object undefined or null properties - forEach(search, function(value, key) { - if (value == null) delete search[key]; - }); - - this.$$search = search; - } else { - throw $locationMinErr('isrcharg', - 'The first argument of the `$location#search()` call must be a string or an object.'); - } - break; - default: - if (isUndefined(paramValue) || paramValue === null) { - delete this.$$search[search]; - } else { - this.$$search[search] = paramValue; - } - } - - this.$$compose(); - return this; - }, - - /** - * @ngdoc method - * @name $location#hash - * - * @description - * This method is getter / setter. - * - * Return hash fragment when called without any parameter. - * - * Change hash fragment when called with parameter and return `$location`. - * - * - * ```js - * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue - * var hash = $location.hash(); - * // => "hashValue" - * ``` - * - * @param {(string|number)=} hash New hash fragment - * @return {string} hash - */ - hash: locationGetterSetter('$$hash', function(hash) { - return hash !== null ? hash.toString() : ''; - }), - - /** - * @ngdoc method - * @name $location#replace - * - * @description - * If called, all changes to $location during current `$digest` will be replacing current history - * record, instead of adding new one. - */ - replace: function() { - this.$$replace = true; - return this; - } -}; - -forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { - Location.prototype = Object.create(locationPrototype); - - /** - * @ngdoc method - * @name $location#state - * - * @description - * This method is getter / setter. - * - * Return the history state object when called without any parameter. - * - * Change the history state object when called with one parameter and return `$location`. - * The state object is later passed to `pushState` or `replaceState`. - * - * NOTE: This method is supported only in HTML5 mode and only in browsers supporting - * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support - * older browsers (like IE9 or Android < 4.0), don't use this method. - * - * @param {object=} state State object for pushState or replaceState - * @return {object} state - */ - Location.prototype.state = function(state) { - if (!arguments.length) { - return this.$$state; - } - - if (Location !== LocationHtml5Url || !this.$$html5) { - throw $locationMinErr('nostate', 'History API state support is available only ' + - 'in HTML5 mode and only in browsers supporting HTML5 History API'); - } - // The user might modify `stateObject` after invoking `$location.state(stateObject)` - // but we're changing the $$state reference to $browser.state() during the $digest - // so the modification window is narrow. - this.$$state = isUndefined(state) ? null : state; - - return this; - }; -}); - - -function locationGetter(property) { - return function() { - return this[property]; - }; -} - - -function locationGetterSetter(property, preprocess) { - return function(value) { - if (isUndefined(value)) { - return this[property]; - } - - this[property] = preprocess(value); - this.$$compose(); - - return this; - }; -} - - -/** - * @ngdoc service - * @name $location - * - * @requires $rootElement - * - * @description - * The $location service parses the URL in the browser address bar (based on the - * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL - * available to your application. Changes to the URL in the address bar are reflected into - * $location service and changes to $location are reflected into the browser address bar. - * - * **The $location service:** - * - * - Exposes the current URL in the browser address bar, so you can - * - Watch and observe the URL. - * - Change the URL. - * - Synchronizes the URL with the browser when the user - * - Changes the address bar. - * - Clicks the back or forward button (or clicks a History link). - * - Clicks on a link. - * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). - * - * For more information see {@link guide/$location Developer Guide: Using $location} - */ - -/** - * @ngdoc provider - * @name $locationProvider - * @description - * Use the `$locationProvider` to configure how the application deep linking paths are stored. - */ -function $LocationProvider() { - var hashPrefix = '', - html5Mode = { - enabled: false, - requireBase: true, - rewriteLinks: true - }; - - /** - * @ngdoc method - * @name $locationProvider#hashPrefix - * @description - * @param {string=} prefix Prefix for hash part (containing path and search) - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.hashPrefix = function(prefix) { - if (isDefined(prefix)) { - hashPrefix = prefix; - return this; - } else { - return hashPrefix; - } - }; - - /** - * @ngdoc method - * @name $locationProvider#html5Mode - * @description - * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. - * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported - * properties: - * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to - * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not - * support `pushState`. - * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies - * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are - * true, and a base tag is not present, an error will be thrown when `$location` is injected. - * See the {@link guide/$location $location guide for more information} - * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, - * enables/disables url rewriting for relative links. - * - * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter - */ - this.html5Mode = function(mode) { - if (isBoolean(mode)) { - html5Mode.enabled = mode; - return this; - } else if (isObject(mode)) { - - if (isBoolean(mode.enabled)) { - html5Mode.enabled = mode.enabled; - } - - if (isBoolean(mode.requireBase)) { - html5Mode.requireBase = mode.requireBase; - } - - if (isBoolean(mode.rewriteLinks)) { - html5Mode.rewriteLinks = mode.rewriteLinks; - } - - return this; - } else { - return html5Mode; - } - }; - - /** - * @ngdoc event - * @name $location#$locationChangeStart - * @eventType broadcast on root scope - * @description - * Broadcasted before a URL will change. - * - * This change can be prevented by calling - * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more - * details about event object. Upon successful change - * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. - * - * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when - * the browser supports the HTML5 History API. - * - * @param {Object} angularEvent Synthetic event object. - * @param {string} newUrl New URL - * @param {string=} oldUrl URL that was before it was changed. - * @param {string=} newState New history state object - * @param {string=} oldState History state object that was before it was changed. - */ - - /** - * @ngdoc event - * @name $location#$locationChangeSuccess - * @eventType broadcast on root scope - * @description - * Broadcasted after a URL was changed. - * - * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when - * the browser supports the HTML5 History API. - * - * @param {Object} angularEvent Synthetic event object. - * @param {string} newUrl New URL - * @param {string=} oldUrl URL that was before it was changed. - * @param {string=} newState New history state object - * @param {string=} oldState History state object that was before it was changed. - */ - - this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', - function($rootScope, $browser, $sniffer, $rootElement, $window) { - var $location, - LocationMode, - baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' - initialUrl = $browser.url(), - appBase; - - if (html5Mode.enabled) { - if (!baseHref && html5Mode.requireBase) { - throw $locationMinErr('nobase', - "$location in HTML5 mode requires a <base> tag to be present!"); - } - appBase = serverBase(initialUrl) + (baseHref || '/'); - LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; - } else { - appBase = stripHash(initialUrl); - LocationMode = LocationHashbangUrl; - } - var appBaseNoFile = stripFile(appBase); - - $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix); - $location.$$parseLinkUrl(initialUrl, initialUrl); - - $location.$$state = $browser.state(); - - var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; - - function setBrowserUrlWithFallback(url, replace, state) { - var oldUrl = $location.url(); - var oldState = $location.$$state; - try { - $browser.url(url, replace, state); - - // Make sure $location.state() returns referentially identical (not just deeply equal) - // state object; this makes possible quick checking if the state changed in the digest - // loop. Checking deep equality would be too expensive. - $location.$$state = $browser.state(); - } catch (e) { - // Restore old values if pushState fails - $location.url(oldUrl); - $location.$$state = oldState; - - throw e; - } - } - - $rootElement.on('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (nodeName_(elm[0]) !== 'a') { - // ignore rewriting if no A tag (reached root element, or no parent - removed from document) - if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; - } - - var absHref = elm.prop('href'); - // get the actual href attribute - see - // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx - var relHref = elm.attr('href') || elm.attr('xlink:href'); - - if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { - // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during - // an animation. - absHref = urlResolve(absHref.animVal).href; - } - - // Ignore when url is started with javascript: or mailto: - if (IGNORE_URI_REGEXP.test(absHref)) return; - - if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { - if ($location.$$parseLinkUrl(absHref, relHref)) { - // We do a preventDefault for all urls that are part of the angular application, - // in html5mode and also without, so that we are able to abort navigation without - // getting double entries in the location history. - event.preventDefault(); - // update location manually - if ($location.absUrl() != $browser.url()) { - $rootScope.$apply(); - // hack to work around FF6 bug 684208 when scenario runner clicks on links - $window.angular['ff-684208-preventDefault'] = true; - } - } - } - }); - - - // rewrite hashbang url <> html5 url - if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) { - $browser.url($location.absUrl(), true); - } - - var initializing = true; - - // update $location when $browser url changes - $browser.onUrlChange(function(newUrl, newState) { - - if (isUndefined(beginsWith(appBaseNoFile, newUrl))) { - // If we are navigating outside of the app then force a reload - $window.location.href = newUrl; - return; - } - - $rootScope.$evalAsync(function() { - var oldUrl = $location.absUrl(); - var oldState = $location.$$state; - var defaultPrevented; - - $location.$$parse(newUrl); - $location.$$state = newState; - - defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - newState, oldState).defaultPrevented; - - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; - - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - setBrowserUrlWithFallback(oldUrl, false, oldState); - } else { - initializing = false; - afterLocationChange(oldUrl, oldState); - } - }); - if (!$rootScope.$$phase) $rootScope.$digest(); - }); - - // update browser - $rootScope.$watch(function $locationWatch() { - var oldUrl = trimEmptyHash($browser.url()); - var newUrl = trimEmptyHash($location.absUrl()); - var oldState = $browser.state(); - var currentReplace = $location.$$replace; - var urlOrStateChanged = oldUrl !== newUrl || - ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); - - if (initializing || urlOrStateChanged) { - initializing = false; - - $rootScope.$evalAsync(function() { - var newUrl = $location.absUrl(); - var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - $location.$$state, oldState).defaultPrevented; - - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; - - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - } else { - if (urlOrStateChanged) { - setBrowserUrlWithFallback(newUrl, currentReplace, - oldState === $location.$$state ? null : $location.$$state); - } - afterLocationChange(oldUrl, oldState); - } - }); - } - - $location.$$replace = false; - - // we don't need to return anything because $evalAsync will make the digest loop dirty when - // there is a change - }); - - return $location; - - function afterLocationChange(oldUrl, oldState) { - $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, - $location.$$state, oldState); - } -}]; -} - -/** - * @ngdoc service - * @name $log - * @requires $window - * - * @description - * Simple service for logging. Default implementation safely writes the message - * into the browser's console (if present). - * - * The main purpose of this service is to simplify debugging and troubleshooting. - * - * The default is to log `debug` messages. You can use - * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. - * - * @example - <example module="logExample"> - <file name="script.js"> - angular.module('logExample', []) - .controller('LogController', ['$scope', '$log', function($scope, $log) { - $scope.$log = $log; - $scope.message = 'Hello World!'; - }]); - </file> - <file name="index.html"> - <div ng-controller="LogController"> - <p>Reload this page with open console, enter text and hit the log button...</p> - <label>Message: - <input type="text" ng-model="message" /></label> - <button ng-click="$log.log(message)">log</button> - <button ng-click="$log.warn(message)">warn</button> - <button ng-click="$log.info(message)">info</button> - <button ng-click="$log.error(message)">error</button> - <button ng-click="$log.debug(message)">debug</button> - </div> - </file> - </example> - */ - -/** - * @ngdoc provider - * @name $logProvider - * @description - * Use the `$logProvider` to configure how the application logs messages - */ -function $LogProvider() { - var debug = true, - self = this; - - /** - * @ngdoc method - * @name $logProvider#debugEnabled - * @description - * @param {boolean=} flag enable or disable debug level messages - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.debugEnabled = function(flag) { - if (isDefined(flag)) { - debug = flag; - return this; - } else { - return debug; - } - }; - - this.$get = ['$window', function($window) { - return { - /** - * @ngdoc method - * @name $log#log - * - * @description - * Write a log message - */ - log: consoleLog('log'), - - /** - * @ngdoc method - * @name $log#info - * - * @description - * Write an information message - */ - info: consoleLog('info'), - - /** - * @ngdoc method - * @name $log#warn - * - * @description - * Write a warning message - */ - warn: consoleLog('warn'), - - /** - * @ngdoc method - * @name $log#error - * - * @description - * Write an error message - */ - error: consoleLog('error'), - - /** - * @ngdoc method - * @name $log#debug - * - * @description - * Write a debug message - */ - debug: (function() { - var fn = consoleLog('debug'); - - return function() { - if (debug) { - fn.apply(self, arguments); - } - }; - }()) - }; - - function formatError(arg) { - if (arg instanceof Error) { - if (arg.stack) { - arg = (arg.message && arg.stack.indexOf(arg.message) === -1) - ? 'Error: ' + arg.message + '\n' + arg.stack - : arg.stack; - } else if (arg.sourceURL) { - arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; - } - } - return arg; - } - - function consoleLog(type) { - var console = $window.console || {}, - logFn = console[type] || console.log || noop, - hasApply = false; - - // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. - // The reason behind this is that console.log has type "object" in IE8... - try { - hasApply = !!logFn.apply; - } catch (e) {} - - if (hasApply) { - return function() { - var args = []; - forEach(arguments, function(arg) { - args.push(formatError(arg)); - }); - return logFn.apply(console, args); - }; - } - - // we are IE which either doesn't have window.console => this is noop and we do nothing, - // or we are IE where console.log doesn't have apply so we log at least first 2 args - return function(arg1, arg2) { - logFn(arg1, arg2 == null ? '' : arg2); - }; - } - }]; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -var $parseMinErr = minErr('$parse'); - -// Sandboxing Angular Expressions -// ------------------------------ -// Angular expressions are generally considered safe because these expressions only have direct -// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by -// obtaining a reference to native JS functions such as the Function constructor. -// -// As an example, consider the following Angular expression: -// -// {}.toString.constructor('alert("evil JS code")') -// -// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits -// against the expression language, but not to prevent exploits that were enabled by exposing -// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good -// practice and therefore we are not even trying to protect against interaction with an object -// explicitly exposed in this way. -// -// In general, it is not possible to access a Window object from an angular expression unless a -// window or some DOM object that has a reference to window is published onto a Scope. -// Similarly we prevent invocations of function known to be dangerous, as well as assignments to -// native objects. -// -// See https://docs.angularjs.org/guide/security - - -function ensureSafeMemberName(name, fullExpression) { - if (name === "__defineGetter__" || name === "__defineSetter__" - || name === "__lookupGetter__" || name === "__lookupSetter__" - || name === "__proto__") { - throw $parseMinErr('isecfld', - 'Attempting to access a disallowed field in Angular expressions! ' - + 'Expression: {0}', fullExpression); - } - return name; -} - -function getStringValue(name, fullExpression) { - // From the JavaScript docs: - // Property names must be strings. This means that non-string objects cannot be used - // as keys in an object. Any non-string object, including a number, is typecasted - // into a string via the toString method. - // - // So, to ensure that we are checking the same `name` that JavaScript would use, - // we cast it to a string, if possible. - // Doing `name + ''` can cause a repl error if the result to `toString` is not a string, - // this is, this will handle objects that misbehave. - name = name + ''; - if (!isString(name)) { - throw $parseMinErr('iseccst', - 'Cannot convert object to primitive value! ' - + 'Expression: {0}', fullExpression); - } - return name; -} - -function ensureSafeObject(obj, fullExpression) { - // nifty check if obj is Function that is fast and works across iframes and other contexts - if (obj) { - if (obj.constructor === obj) { - throw $parseMinErr('isecfn', - 'Referencing Function in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// isWindow(obj) - obj.window === obj) { - throw $parseMinErr('isecwindow', - 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// isElement(obj) - obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { - throw $parseMinErr('isecdom', - 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (// block Object so that we can't get hold of dangerous Object.* methods - obj === Object) { - throw $parseMinErr('isecobj', - 'Referencing Object in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } - } - return obj; -} - -var CALL = Function.prototype.call; -var APPLY = Function.prototype.apply; -var BIND = Function.prototype.bind; - -function ensureSafeFunction(obj, fullExpression) { - if (obj) { - if (obj.constructor === obj) { - throw $parseMinErr('isecfn', - 'Referencing Function in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } else if (obj === CALL || obj === APPLY || obj === BIND) { - throw $parseMinErr('isecff', - 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', - fullExpression); - } - } -} - -function ensureSafeAssignContext(obj, fullExpression) { - if (obj) { - if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor || - obj === {}.constructor || obj === [].constructor || obj === Function.constructor) { - throw $parseMinErr('isecaf', - 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression); - } - } -} - -var OPERATORS = createMap(); -forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); -var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; - - -///////////////////////////////////////// - - -/** - * @constructor - */ -var Lexer = function(options) { - this.options = options; -}; - -Lexer.prototype = { - constructor: Lexer, - - lex: function(text) { - this.text = text; - this.index = 0; - this.tokens = []; - - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (ch === '"' || ch === "'") { - this.readString(ch); - } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { - this.readNumber(); - } else if (this.isIdent(ch)) { - this.readIdent(); - } else if (this.is(ch, '(){}[].,;:?')) { - this.tokens.push({index: this.index, text: ch}); - this.index++; - } else if (this.isWhitespace(ch)) { - this.index++; - } else { - var ch2 = ch + this.peek(); - var ch3 = ch2 + this.peek(2); - var op1 = OPERATORS[ch]; - var op2 = OPERATORS[ch2]; - var op3 = OPERATORS[ch3]; - if (op1 || op2 || op3) { - var token = op3 ? ch3 : (op2 ? ch2 : ch); - this.tokens.push({index: this.index, text: token, operator: true}); - this.index += token.length; - } else { - this.throwError('Unexpected next character ', this.index, this.index + 1); - } - } - } - return this.tokens; - }, - - is: function(ch, chars) { - return chars.indexOf(ch) !== -1; - }, - - peek: function(i) { - var num = i || 1; - return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; - }, - - isNumber: function(ch) { - return ('0' <= ch && ch <= '9') && typeof ch === "string"; - }, - - isWhitespace: function(ch) { - // IE treats non-breaking space as \u00A0 - return (ch === ' ' || ch === '\r' || ch === '\t' || - ch === '\n' || ch === '\v' || ch === '\u00A0'); - }, - - isIdent: function(ch) { - return ('a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' === ch || ch === '$'); - }, - - isExpOperator: function(ch) { - return (ch === '-' || ch === '+' || this.isNumber(ch)); - }, - - throwError: function(error, start, end) { - end = end || this.index; - var colStr = (isDefined(start) - ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' - : ' ' + end); - throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', - error, colStr, this.text); - }, - - readNumber: function() { - var number = ''; - var start = this.index; - while (this.index < this.text.length) { - var ch = lowercase(this.text.charAt(this.index)); - if (ch == '.' || this.isNumber(ch)) { - number += ch; - } else { - var peekCh = this.peek(); - if (ch == 'e' && this.isExpOperator(peekCh)) { - number += ch; - } else if (this.isExpOperator(ch) && - peekCh && this.isNumber(peekCh) && - number.charAt(number.length - 1) == 'e') { - number += ch; - } else if (this.isExpOperator(ch) && - (!peekCh || !this.isNumber(peekCh)) && - number.charAt(number.length - 1) == 'e') { - this.throwError('Invalid exponent'); - } else { - break; - } - } - this.index++; - } - this.tokens.push({ - index: start, - text: number, - constant: true, - value: Number(number) - }); - }, - - readIdent: function() { - var start = this.index; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (!(this.isIdent(ch) || this.isNumber(ch))) { - break; - } - this.index++; - } - this.tokens.push({ - index: start, - text: this.text.slice(start, this.index), - identifier: true - }); - }, - - readString: function(quote) { - var start = this.index; - this.index++; - var string = ''; - var rawString = quote; - var escape = false; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - rawString += ch; - if (escape) { - if (ch === 'u') { - var hex = this.text.substring(this.index + 1, this.index + 5); - if (!hex.match(/[\da-f]{4}/i)) { - this.throwError('Invalid unicode escape [\\u' + hex + ']'); - } - this.index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = ESCAPE[ch]; - string = string + (rep || ch); - } - escape = false; - } else if (ch === '\\') { - escape = true; - } else if (ch === quote) { - this.index++; - this.tokens.push({ - index: start, - text: rawString, - constant: true, - value: string - }); - return; - } else { - string += ch; - } - this.index++; - } - this.throwError('Unterminated quote', start); - } -}; - -var AST = function(lexer, options) { - this.lexer = lexer; - this.options = options; -}; - -AST.Program = 'Program'; -AST.ExpressionStatement = 'ExpressionStatement'; -AST.AssignmentExpression = 'AssignmentExpression'; -AST.ConditionalExpression = 'ConditionalExpression'; -AST.LogicalExpression = 'LogicalExpression'; -AST.BinaryExpression = 'BinaryExpression'; -AST.UnaryExpression = 'UnaryExpression'; -AST.CallExpression = 'CallExpression'; -AST.MemberExpression = 'MemberExpression'; -AST.Identifier = 'Identifier'; -AST.Literal = 'Literal'; -AST.ArrayExpression = 'ArrayExpression'; -AST.Property = 'Property'; -AST.ObjectExpression = 'ObjectExpression'; -AST.ThisExpression = 'ThisExpression'; - -// Internal use only -AST.NGValueParameter = 'NGValueParameter'; - -AST.prototype = { - ast: function(text) { - this.text = text; - this.tokens = this.lexer.lex(text); - - var value = this.program(); - - if (this.tokens.length !== 0) { - this.throwError('is an unexpected token', this.tokens[0]); - } - - return value; - }, - - program: function() { - var body = []; - while (true) { - if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) - body.push(this.expressionStatement()); - if (!this.expect(';')) { - return { type: AST.Program, body: body}; - } - } - }, - - expressionStatement: function() { - return { type: AST.ExpressionStatement, expression: this.filterChain() }; - }, - - filterChain: function() { - var left = this.expression(); - var token; - while ((token = this.expect('|'))) { - left = this.filter(left); - } - return left; - }, - - expression: function() { - return this.assignment(); - }, - - assignment: function() { - var result = this.ternary(); - if (this.expect('=')) { - result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='}; - } - return result; - }, - - ternary: function() { - var test = this.logicalOR(); - var alternate; - var consequent; - if (this.expect('?')) { - alternate = this.expression(); - if (this.consume(':')) { - consequent = this.expression(); - return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent}; - } - } - return test; - }, - - logicalOR: function() { - var left = this.logicalAND(); - while (this.expect('||')) { - left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() }; - } - return left; - }, - - logicalAND: function() { - var left = this.equality(); - while (this.expect('&&')) { - left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()}; - } - return left; - }, - - equality: function() { - var left = this.relational(); - var token; - while ((token = this.expect('==','!=','===','!=='))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() }; - } - return left; - }, - - relational: function() { - var left = this.additive(); - var token; - while ((token = this.expect('<', '>', '<=', '>='))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() }; - } - return left; - }, - - additive: function() { - var left = this.multiplicative(); - var token; - while ((token = this.expect('+','-'))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() }; - } - return left; - }, - - multiplicative: function() { - var left = this.unary(); - var token; - while ((token = this.expect('*','/','%'))) { - left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() }; - } - return left; - }, - - unary: function() { - var token; - if ((token = this.expect('+', '-', '!'))) { - return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() }; - } else { - return this.primary(); - } - }, - - primary: function() { - var primary; - if (this.expect('(')) { - primary = this.filterChain(); - this.consume(')'); - } else if (this.expect('[')) { - primary = this.arrayDeclaration(); - } else if (this.expect('{')) { - primary = this.object(); - } else if (this.constants.hasOwnProperty(this.peek().text)) { - primary = copy(this.constants[this.consume().text]); - } else if (this.peek().identifier) { - primary = this.identifier(); - } else if (this.peek().constant) { - primary = this.constant(); - } else { - this.throwError('not a primary expression', this.peek()); - } - - var next; - while ((next = this.expect('(', '[', '.'))) { - if (next.text === '(') { - primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() }; - this.consume(')'); - } else if (next.text === '[') { - primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true }; - this.consume(']'); - } else if (next.text === '.') { - primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false }; - } else { - this.throwError('IMPOSSIBLE'); - } - } - return primary; - }, - - filter: function(baseExpression) { - var args = [baseExpression]; - var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true}; - - while (this.expect(':')) { - args.push(this.expression()); - } - - return result; - }, - - parseArguments: function() { - var args = []; - if (this.peekToken().text !== ')') { - do { - args.push(this.expression()); - } while (this.expect(',')); - } - return args; - }, - - identifier: function() { - var token = this.consume(); - if (!token.identifier) { - this.throwError('is not a valid identifier', token); - } - return { type: AST.Identifier, name: token.text }; - }, - - constant: function() { - // TODO check that it is a constant - return { type: AST.Literal, value: this.consume().value }; - }, - - arrayDeclaration: function() { - var elements = []; - if (this.peekToken().text !== ']') { - do { - if (this.peek(']')) { - // Support trailing commas per ES5.1. - break; - } - elements.push(this.expression()); - } while (this.expect(',')); - } - this.consume(']'); - - return { type: AST.ArrayExpression, elements: elements }; - }, - - object: function() { - var properties = [], property; - if (this.peekToken().text !== '}') { - do { - if (this.peek('}')) { - // Support trailing commas per ES5.1. - break; - } - property = {type: AST.Property, kind: 'init'}; - if (this.peek().constant) { - property.key = this.constant(); - } else if (this.peek().identifier) { - property.key = this.identifier(); - } else { - this.throwError("invalid key", this.peek()); - } - this.consume(':'); - property.value = this.expression(); - properties.push(property); - } while (this.expect(',')); - } - this.consume('}'); - - return {type: AST.ObjectExpression, properties: properties }; - }, - - throwError: function(msg, token) { - throw $parseMinErr('syntax', - 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', - token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); - }, - - consume: function(e1) { - if (this.tokens.length === 0) { - throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); - } - - var token = this.expect(e1); - if (!token) { - this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); - } - return token; - }, - - peekToken: function() { - if (this.tokens.length === 0) { - throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); - } - return this.tokens[0]; - }, - - peek: function(e1, e2, e3, e4) { - return this.peekAhead(0, e1, e2, e3, e4); - }, - - peekAhead: function(i, e1, e2, e3, e4) { - if (this.tokens.length > i) { - var token = this.tokens[i]; - var t = token.text; - if (t === e1 || t === e2 || t === e3 || t === e4 || - (!e1 && !e2 && !e3 && !e4)) { - return token; - } - } - return false; - }, - - expect: function(e1, e2, e3, e4) { - var token = this.peek(e1, e2, e3, e4); - if (token) { - this.tokens.shift(); - return token; - } - return false; - }, - - - /* `undefined` is not a constant, it is an identifier, - * but using it as an identifier is not supported - */ - constants: { - 'true': { type: AST.Literal, value: true }, - 'false': { type: AST.Literal, value: false }, - 'null': { type: AST.Literal, value: null }, - 'undefined': {type: AST.Literal, value: undefined }, - 'this': {type: AST.ThisExpression } - } -}; - -function ifDefined(v, d) { - return typeof v !== 'undefined' ? v : d; -} - -function plusFn(l, r) { - if (typeof l === 'undefined') return r; - if (typeof r === 'undefined') return l; - return l + r; -} - -function isStateless($filter, filterName) { - var fn = $filter(filterName); - return !fn.$stateful; -} - -function findConstantAndWatchExpressions(ast, $filter) { - var allConstants; - var argsToWatch; - switch (ast.type) { - case AST.Program: - allConstants = true; - forEach(ast.body, function(expr) { - findConstantAndWatchExpressions(expr.expression, $filter); - allConstants = allConstants && expr.expression.constant; - }); - ast.constant = allConstants; - break; - case AST.Literal: - ast.constant = true; - ast.toWatch = []; - break; - case AST.UnaryExpression: - findConstantAndWatchExpressions(ast.argument, $filter); - ast.constant = ast.argument.constant; - ast.toWatch = ast.argument.toWatch; - break; - case AST.BinaryExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch); - break; - case AST.LogicalExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = ast.constant ? [] : [ast]; - break; - case AST.ConditionalExpression: - findConstantAndWatchExpressions(ast.test, $filter); - findConstantAndWatchExpressions(ast.alternate, $filter); - findConstantAndWatchExpressions(ast.consequent, $filter); - ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant; - ast.toWatch = ast.constant ? [] : [ast]; - break; - case AST.Identifier: - ast.constant = false; - ast.toWatch = [ast]; - break; - case AST.MemberExpression: - findConstantAndWatchExpressions(ast.object, $filter); - if (ast.computed) { - findConstantAndWatchExpressions(ast.property, $filter); - } - ast.constant = ast.object.constant && (!ast.computed || ast.property.constant); - ast.toWatch = [ast]; - break; - case AST.CallExpression: - allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false; - argsToWatch = []; - forEach(ast.arguments, function(expr) { - findConstantAndWatchExpressions(expr, $filter); - allConstants = allConstants && expr.constant; - if (!expr.constant) { - argsToWatch.push.apply(argsToWatch, expr.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast]; - break; - case AST.AssignmentExpression: - findConstantAndWatchExpressions(ast.left, $filter); - findConstantAndWatchExpressions(ast.right, $filter); - ast.constant = ast.left.constant && ast.right.constant; - ast.toWatch = [ast]; - break; - case AST.ArrayExpression: - allConstants = true; - argsToWatch = []; - forEach(ast.elements, function(expr) { - findConstantAndWatchExpressions(expr, $filter); - allConstants = allConstants && expr.constant; - if (!expr.constant) { - argsToWatch.push.apply(argsToWatch, expr.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = argsToWatch; - break; - case AST.ObjectExpression: - allConstants = true; - argsToWatch = []; - forEach(ast.properties, function(property) { - findConstantAndWatchExpressions(property.value, $filter); - allConstants = allConstants && property.value.constant; - if (!property.value.constant) { - argsToWatch.push.apply(argsToWatch, property.value.toWatch); - } - }); - ast.constant = allConstants; - ast.toWatch = argsToWatch; - break; - case AST.ThisExpression: - ast.constant = false; - ast.toWatch = []; - break; - } -} - -function getInputs(body) { - if (body.length != 1) return; - var lastExpression = body[0].expression; - var candidate = lastExpression.toWatch; - if (candidate.length !== 1) return candidate; - return candidate[0] !== lastExpression ? candidate : undefined; -} - -function isAssignable(ast) { - return ast.type === AST.Identifier || ast.type === AST.MemberExpression; -} - -function assignableAST(ast) { - if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) { - return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='}; - } -} - -function isLiteral(ast) { - return ast.body.length === 0 || - ast.body.length === 1 && ( - ast.body[0].expression.type === AST.Literal || - ast.body[0].expression.type === AST.ArrayExpression || - ast.body[0].expression.type === AST.ObjectExpression); -} - -function isConstant(ast) { - return ast.constant; -} - -function ASTCompiler(astBuilder, $filter) { - this.astBuilder = astBuilder; - this.$filter = $filter; -} - -ASTCompiler.prototype = { - compile: function(expression, expensiveChecks) { - var self = this; - var ast = this.astBuilder.ast(expression); - this.state = { - nextId: 0, - filters: {}, - expensiveChecks: expensiveChecks, - fn: {vars: [], body: [], own: {}}, - assign: {vars: [], body: [], own: {}}, - inputs: [] - }; - findConstantAndWatchExpressions(ast, self.$filter); - var extra = ''; - var assignable; - this.stage = 'assign'; - if ((assignable = assignableAST(ast))) { - this.state.computing = 'assign'; - var result = this.nextId(); - this.recurse(assignable, result); - this.return_(result); - extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l'); - } - var toWatch = getInputs(ast.body); - self.stage = 'inputs'; - forEach(toWatch, function(watch, key) { - var fnKey = 'fn' + key; - self.state[fnKey] = {vars: [], body: [], own: {}}; - self.state.computing = fnKey; - var intoId = self.nextId(); - self.recurse(watch, intoId); - self.return_(intoId); - self.state.inputs.push(fnKey); - watch.watchId = key; - }); - this.state.computing = 'fn'; - this.stage = 'main'; - this.recurse(ast); - var fnString = - // The build and minification steps remove the string "use strict" from the code, but this is done using a regex. - // This is a workaround for this until we do a better job at only removing the prefix only when we should. - '"' + this.USE + ' ' + this.STRICT + '";\n' + - this.filterPrefix() + - 'var fn=' + this.generateFunction('fn', 's,l,a,i') + - extra + - this.watchFns() + - 'return fn;'; - - /* jshint -W054 */ - var fn = (new Function('$filter', - 'ensureSafeMemberName', - 'ensureSafeObject', - 'ensureSafeFunction', - 'getStringValue', - 'ensureSafeAssignContext', - 'ifDefined', - 'plus', - 'text', - fnString))( - this.$filter, - ensureSafeMemberName, - ensureSafeObject, - ensureSafeFunction, - getStringValue, - ensureSafeAssignContext, - ifDefined, - plusFn, - expression); - /* jshint +W054 */ - this.state = this.stage = undefined; - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); - return fn; - }, - - USE: 'use', - - STRICT: 'strict', - - watchFns: function() { - var result = []; - var fns = this.state.inputs; - var self = this; - forEach(fns, function(name) { - result.push('var ' + name + '=' + self.generateFunction(name, 's')); - }); - if (fns.length) { - result.push('fn.inputs=[' + fns.join(',') + '];'); - } - return result.join(''); - }, - - generateFunction: function(name, params) { - return 'function(' + params + '){' + - this.varsPrefix(name) + - this.body(name) + - '};'; - }, - - filterPrefix: function() { - var parts = []; - var self = this; - forEach(this.state.filters, function(id, filter) { - parts.push(id + '=$filter(' + self.escape(filter) + ')'); - }); - if (parts.length) return 'var ' + parts.join(',') + ';'; - return ''; - }, - - varsPrefix: function(section) { - return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : ''; - }, - - body: function(section) { - return this.state[section].body.join(''); - }, - - recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { - var left, right, self = this, args, expression; - recursionFn = recursionFn || noop; - if (!skipWatchIdCheck && isDefined(ast.watchId)) { - intoId = intoId || this.nextId(); - this.if_('i', - this.lazyAssign(intoId, this.computedMember('i', ast.watchId)), - this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true) - ); - return; - } - switch (ast.type) { - case AST.Program: - forEach(ast.body, function(expression, pos) { - self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; }); - if (pos !== ast.body.length - 1) { - self.current().body.push(right, ';'); - } else { - self.return_(right); - } - }); - break; - case AST.Literal: - expression = this.escape(ast.value); - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.UnaryExpression: - this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; }); - expression = ast.operator + '(' + this.ifDefined(right, 0) + ')'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.BinaryExpression: - this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; }); - this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; }); - if (ast.operator === '+') { - expression = this.plus(left, right); - } else if (ast.operator === '-') { - expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0); - } else { - expression = '(' + left + ')' + ast.operator + '(' + right + ')'; - } - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.LogicalExpression: - intoId = intoId || this.nextId(); - self.recurse(ast.left, intoId); - self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId)); - recursionFn(intoId); - break; - case AST.ConditionalExpression: - intoId = intoId || this.nextId(); - self.recurse(ast.test, intoId); - self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId)); - recursionFn(intoId); - break; - case AST.Identifier: - intoId = intoId || this.nextId(); - if (nameId) { - nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s'); - nameId.computed = false; - nameId.name = ast.name; - } - ensureSafeMemberName(ast.name); - self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)), - function() { - self.if_(self.stage === 'inputs' || 's', function() { - if (create && create !== 1) { - self.if_( - self.not(self.nonComputedMember('s', ast.name)), - self.lazyAssign(self.nonComputedMember('s', ast.name), '{}')); - } - self.assign(intoId, self.nonComputedMember('s', ast.name)); - }); - }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name)) - ); - if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) { - self.addEnsureSafeObject(intoId); - } - recursionFn(intoId); - break; - case AST.MemberExpression: - left = nameId && (nameId.context = this.nextId()) || this.nextId(); - intoId = intoId || this.nextId(); - self.recurse(ast.object, left, undefined, function() { - self.if_(self.notNull(left), function() { - if (ast.computed) { - right = self.nextId(); - self.recurse(ast.property, right); - self.getStringValue(right); - self.addEnsureSafeMemberName(right); - if (create && create !== 1) { - self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}')); - } - expression = self.ensureSafeObject(self.computedMember(left, right)); - self.assign(intoId, expression); - if (nameId) { - nameId.computed = true; - nameId.name = right; - } - } else { - ensureSafeMemberName(ast.property.name); - if (create && create !== 1) { - self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}')); - } - expression = self.nonComputedMember(left, ast.property.name); - if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) { - expression = self.ensureSafeObject(expression); - } - self.assign(intoId, expression); - if (nameId) { - nameId.computed = false; - nameId.name = ast.property.name; - } - } - }, function() { - self.assign(intoId, 'undefined'); - }); - recursionFn(intoId); - }, !!create); - break; - case AST.CallExpression: - intoId = intoId || this.nextId(); - if (ast.filter) { - right = self.filter(ast.callee.name); - args = []; - forEach(ast.arguments, function(expr) { - var argument = self.nextId(); - self.recurse(expr, argument); - args.push(argument); - }); - expression = right + '(' + args.join(',') + ')'; - self.assign(intoId, expression); - recursionFn(intoId); - } else { - right = self.nextId(); - left = {}; - args = []; - self.recurse(ast.callee, right, left, function() { - self.if_(self.notNull(right), function() { - self.addEnsureSafeFunction(right); - forEach(ast.arguments, function(expr) { - self.recurse(expr, self.nextId(), undefined, function(argument) { - args.push(self.ensureSafeObject(argument)); - }); - }); - if (left.name) { - if (!self.state.expensiveChecks) { - self.addEnsureSafeObject(left.context); - } - expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')'; - } else { - expression = right + '(' + args.join(',') + ')'; - } - expression = self.ensureSafeObject(expression); - self.assign(intoId, expression); - }, function() { - self.assign(intoId, 'undefined'); - }); - recursionFn(intoId); - }); - } - break; - case AST.AssignmentExpression: - right = this.nextId(); - left = {}; - if (!isAssignable(ast.left)) { - throw $parseMinErr('lval', 'Trying to assing a value to a non l-value'); - } - this.recurse(ast.left, undefined, left, function() { - self.if_(self.notNull(left.context), function() { - self.recurse(ast.right, right); - self.addEnsureSafeObject(self.member(left.context, left.name, left.computed)); - self.addEnsureSafeAssignContext(left.context); - expression = self.member(left.context, left.name, left.computed) + ast.operator + right; - self.assign(intoId, expression); - recursionFn(intoId || expression); - }); - }, 1); - break; - case AST.ArrayExpression: - args = []; - forEach(ast.elements, function(expr) { - self.recurse(expr, self.nextId(), undefined, function(argument) { - args.push(argument); - }); - }); - expression = '[' + args.join(',') + ']'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.ObjectExpression: - args = []; - forEach(ast.properties, function(property) { - self.recurse(property.value, self.nextId(), undefined, function(expr) { - args.push(self.escape( - property.key.type === AST.Identifier ? property.key.name : - ('' + property.key.value)) + - ':' + expr); - }); - }); - expression = '{' + args.join(',') + '}'; - this.assign(intoId, expression); - recursionFn(expression); - break; - case AST.ThisExpression: - this.assign(intoId, 's'); - recursionFn('s'); - break; - case AST.NGValueParameter: - this.assign(intoId, 'v'); - recursionFn('v'); - break; - } - }, - - getHasOwnProperty: function(element, property) { - var key = element + '.' + property; - var own = this.current().own; - if (!own.hasOwnProperty(key)) { - own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')'); - } - return own[key]; - }, - - assign: function(id, value) { - if (!id) return; - this.current().body.push(id, '=', value, ';'); - return id; - }, - - filter: function(filterName) { - if (!this.state.filters.hasOwnProperty(filterName)) { - this.state.filters[filterName] = this.nextId(true); - } - return this.state.filters[filterName]; - }, - - ifDefined: function(id, defaultValue) { - return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')'; - }, - - plus: function(left, right) { - return 'plus(' + left + ',' + right + ')'; - }, - - return_: function(id) { - this.current().body.push('return ', id, ';'); - }, - - if_: function(test, alternate, consequent) { - if (test === true) { - alternate(); - } else { - var body = this.current().body; - body.push('if(', test, '){'); - alternate(); - body.push('}'); - if (consequent) { - body.push('else{'); - consequent(); - body.push('}'); - } - } - }, - - not: function(expression) { - return '!(' + expression + ')'; - }, - - notNull: function(expression) { - return expression + '!=null'; - }, - - nonComputedMember: function(left, right) { - return left + '.' + right; - }, - - computedMember: function(left, right) { - return left + '[' + right + ']'; - }, - - member: function(left, right, computed) { - if (computed) return this.computedMember(left, right); - return this.nonComputedMember(left, right); - }, - - addEnsureSafeObject: function(item) { - this.current().body.push(this.ensureSafeObject(item), ';'); - }, - - addEnsureSafeMemberName: function(item) { - this.current().body.push(this.ensureSafeMemberName(item), ';'); - }, - - addEnsureSafeFunction: function(item) { - this.current().body.push(this.ensureSafeFunction(item), ';'); - }, - - addEnsureSafeAssignContext: function(item) { - this.current().body.push(this.ensureSafeAssignContext(item), ';'); - }, - - ensureSafeObject: function(item) { - return 'ensureSafeObject(' + item + ',text)'; - }, - - ensureSafeMemberName: function(item) { - return 'ensureSafeMemberName(' + item + ',text)'; - }, - - ensureSafeFunction: function(item) { - return 'ensureSafeFunction(' + item + ',text)'; - }, - - getStringValue: function(item) { - this.assign(item, 'getStringValue(' + item + ',text)'); - }, - - ensureSafeAssignContext: function(item) { - return 'ensureSafeAssignContext(' + item + ',text)'; - }, - - lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { - var self = this; - return function() { - self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck); - }; - }, - - lazyAssign: function(id, value) { - var self = this; - return function() { - self.assign(id, value); - }; - }, - - stringEscapeRegex: /[^ a-zA-Z0-9]/g, - - stringEscapeFn: function(c) { - return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); - }, - - escape: function(value) { - if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'"; - if (isNumber(value)) return value.toString(); - if (value === true) return 'true'; - if (value === false) return 'false'; - if (value === null) return 'null'; - if (typeof value === 'undefined') return 'undefined'; - - throw $parseMinErr('esc', 'IMPOSSIBLE'); - }, - - nextId: function(skip, init) { - var id = 'v' + (this.state.nextId++); - if (!skip) { - this.current().vars.push(id + (init ? '=' + init : '')); - } - return id; - }, - - current: function() { - return this.state[this.state.computing]; - } -}; - - -function ASTInterpreter(astBuilder, $filter) { - this.astBuilder = astBuilder; - this.$filter = $filter; -} - -ASTInterpreter.prototype = { - compile: function(expression, expensiveChecks) { - var self = this; - var ast = this.astBuilder.ast(expression); - this.expression = expression; - this.expensiveChecks = expensiveChecks; - findConstantAndWatchExpressions(ast, self.$filter); - var assignable; - var assign; - if ((assignable = assignableAST(ast))) { - assign = this.recurse(assignable); - } - var toWatch = getInputs(ast.body); - var inputs; - if (toWatch) { - inputs = []; - forEach(toWatch, function(watch, key) { - var input = self.recurse(watch); - watch.input = input; - inputs.push(input); - watch.watchId = key; - }); - } - var expressions = []; - forEach(ast.body, function(expression) { - expressions.push(self.recurse(expression.expression)); - }); - var fn = ast.body.length === 0 ? function() {} : - ast.body.length === 1 ? expressions[0] : - function(scope, locals) { - var lastValue; - forEach(expressions, function(exp) { - lastValue = exp(scope, locals); - }); - return lastValue; - }; - if (assign) { - fn.assign = function(scope, value, locals) { - return assign(scope, locals, value); - }; - } - if (inputs) { - fn.inputs = inputs; - } - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); - return fn; - }, - - recurse: function(ast, context, create) { - var left, right, self = this, args, expression; - if (ast.input) { - return this.inputs(ast.input, ast.watchId); - } - switch (ast.type) { - case AST.Literal: - return this.value(ast.value, context); - case AST.UnaryExpression: - right = this.recurse(ast.argument); - return this['unary' + ast.operator](right, context); - case AST.BinaryExpression: - left = this.recurse(ast.left); - right = this.recurse(ast.right); - return this['binary' + ast.operator](left, right, context); - case AST.LogicalExpression: - left = this.recurse(ast.left); - right = this.recurse(ast.right); - return this['binary' + ast.operator](left, right, context); - case AST.ConditionalExpression: - return this['ternary?:']( - this.recurse(ast.test), - this.recurse(ast.alternate), - this.recurse(ast.consequent), - context - ); - case AST.Identifier: - ensureSafeMemberName(ast.name, self.expression); - return self.identifier(ast.name, - self.expensiveChecks || isPossiblyDangerousMemberName(ast.name), - context, create, self.expression); - case AST.MemberExpression: - left = this.recurse(ast.object, false, !!create); - if (!ast.computed) { - ensureSafeMemberName(ast.property.name, self.expression); - right = ast.property.name; - } - if (ast.computed) right = this.recurse(ast.property); - return ast.computed ? - this.computedMember(left, right, context, create, self.expression) : - this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression); - case AST.CallExpression: - args = []; - forEach(ast.arguments, function(expr) { - args.push(self.recurse(expr)); - }); - if (ast.filter) right = this.$filter(ast.callee.name); - if (!ast.filter) right = this.recurse(ast.callee, true); - return ast.filter ? - function(scope, locals, assign, inputs) { - var values = []; - for (var i = 0; i < args.length; ++i) { - values.push(args[i](scope, locals, assign, inputs)); - } - var value = right.apply(undefined, values, inputs); - return context ? {context: undefined, name: undefined, value: value} : value; - } : - function(scope, locals, assign, inputs) { - var rhs = right(scope, locals, assign, inputs); - var value; - if (rhs.value != null) { - ensureSafeObject(rhs.context, self.expression); - ensureSafeFunction(rhs.value, self.expression); - var values = []; - for (var i = 0; i < args.length; ++i) { - values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression)); - } - value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression); - } - return context ? {value: value} : value; - }; - case AST.AssignmentExpression: - left = this.recurse(ast.left, true, 1); - right = this.recurse(ast.right); - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - ensureSafeObject(lhs.value, self.expression); - ensureSafeAssignContext(lhs.context); - lhs.context[lhs.name] = rhs; - return context ? {value: rhs} : rhs; - }; - case AST.ArrayExpression: - args = []; - forEach(ast.elements, function(expr) { - args.push(self.recurse(expr)); - }); - return function(scope, locals, assign, inputs) { - var value = []; - for (var i = 0; i < args.length; ++i) { - value.push(args[i](scope, locals, assign, inputs)); - } - return context ? {value: value} : value; - }; - case AST.ObjectExpression: - args = []; - forEach(ast.properties, function(property) { - args.push({key: property.key.type === AST.Identifier ? - property.key.name : - ('' + property.key.value), - value: self.recurse(property.value) - }); - }); - return function(scope, locals, assign, inputs) { - var value = {}; - for (var i = 0; i < args.length; ++i) { - value[args[i].key] = args[i].value(scope, locals, assign, inputs); - } - return context ? {value: value} : value; - }; - case AST.ThisExpression: - return function(scope) { - return context ? {value: scope} : scope; - }; - case AST.NGValueParameter: - return function(scope, locals, assign, inputs) { - return context ? {value: assign} : assign; - }; - } - }, - - 'unary+': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = argument(scope, locals, assign, inputs); - if (isDefined(arg)) { - arg = +arg; - } else { - arg = 0; - } - return context ? {value: arg} : arg; - }; - }, - 'unary-': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = argument(scope, locals, assign, inputs); - if (isDefined(arg)) { - arg = -arg; - } else { - arg = 0; - } - return context ? {value: arg} : arg; - }; - }, - 'unary!': function(argument, context) { - return function(scope, locals, assign, inputs) { - var arg = !argument(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary+': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - var arg = plusFn(lhs, rhs); - return context ? {value: arg} : arg; - }; - }, - 'binary-': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs = right(scope, locals, assign, inputs); - var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0); - return context ? {value: arg} : arg; - }; - }, - 'binary*': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary/': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary%': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary===': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary!==': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary==': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary!=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary<': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary>': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary<=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary>=': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary&&': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'binary||': function(left, right, context) { - return function(scope, locals, assign, inputs) { - var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - 'ternary?:': function(test, alternate, consequent, context) { - return function(scope, locals, assign, inputs) { - var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs); - return context ? {value: arg} : arg; - }; - }, - value: function(value, context) { - return function() { return context ? {context: undefined, name: undefined, value: value} : value; }; - }, - identifier: function(name, expensiveChecks, context, create, expression) { - return function(scope, locals, assign, inputs) { - var base = locals && (name in locals) ? locals : scope; - if (create && create !== 1 && base && !(base[name])) { - base[name] = {}; - } - var value = base ? base[name] : undefined; - if (expensiveChecks) { - ensureSafeObject(value, expression); - } - if (context) { - return {context: base, name: name, value: value}; - } else { - return value; - } - }; - }, - computedMember: function(left, right, context, create, expression) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - var rhs; - var value; - if (lhs != null) { - rhs = right(scope, locals, assign, inputs); - rhs = getStringValue(rhs); - ensureSafeMemberName(rhs, expression); - if (create && create !== 1 && lhs && !(lhs[rhs])) { - lhs[rhs] = {}; - } - value = lhs[rhs]; - ensureSafeObject(value, expression); - } - if (context) { - return {context: lhs, name: rhs, value: value}; - } else { - return value; - } - }; - }, - nonComputedMember: function(left, right, expensiveChecks, context, create, expression) { - return function(scope, locals, assign, inputs) { - var lhs = left(scope, locals, assign, inputs); - if (create && create !== 1 && lhs && !(lhs[right])) { - lhs[right] = {}; - } - var value = lhs != null ? lhs[right] : undefined; - if (expensiveChecks || isPossiblyDangerousMemberName(right)) { - ensureSafeObject(value, expression); - } - if (context) { - return {context: lhs, name: right, value: value}; - } else { - return value; - } - }; - }, - inputs: function(input, watchId) { - return function(scope, value, locals, inputs) { - if (inputs) return inputs[watchId]; - return input(scope, value, locals); - }; - } -}; - -/** - * @constructor - */ -var Parser = function(lexer, $filter, options) { - this.lexer = lexer; - this.$filter = $filter; - this.options = options; - this.ast = new AST(this.lexer); - this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : - new ASTCompiler(this.ast, $filter); -}; - -Parser.prototype = { - constructor: Parser, - - parse: function(text) { - return this.astCompiler.compile(text, this.options.expensiveChecks); - } -}; - -var getterFnCacheDefault = createMap(); -var getterFnCacheExpensive = createMap(); - -function isPossiblyDangerousMemberName(name) { - return name == 'constructor'; -} - -var objectValueOf = Object.prototype.valueOf; - -function getValueOf(value) { - return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); -} - -/////////////////////////////////// - -/** - * @ngdoc service - * @name $parse - * @kind function - * - * @description - * - * Converts Angular {@link guide/expression expression} into a function. - * - * ```js - * var getter = $parse('user.name'); - * var setter = getter.assign; - * var context = {user:{name:'angular'}}; - * var locals = {user:{name:'local'}}; - * - * expect(getter(context)).toEqual('angular'); - * setter(context, 'newValue'); - * expect(context.user.name).toEqual('newValue'); - * expect(getter(context, locals)).toEqual('local'); - * ``` - * - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - * - * The returned function also has the following properties: - * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript - * literal. - * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript - * constant literals. - * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be - * set to a function to change its value on the given context. - * - */ - - -/** - * @ngdoc provider - * @name $parseProvider - * - * @description - * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} - * service. - */ -function $ParseProvider() { - var cacheDefault = createMap(); - var cacheExpensive = createMap(); - - this.$get = ['$filter', function($filter) { - var noUnsafeEval = csp().noUnsafeEval; - var $parseOptions = { - csp: noUnsafeEval, - expensiveChecks: false - }, - $parseOptionsExpensive = { - csp: noUnsafeEval, - expensiveChecks: true - }; - - return function $parse(exp, interceptorFn, expensiveChecks) { - var parsedExpression, oneTime, cacheKey; - - switch (typeof exp) { - case 'string': - exp = exp.trim(); - cacheKey = exp; - - var cache = (expensiveChecks ? cacheExpensive : cacheDefault); - parsedExpression = cache[cacheKey]; - - if (!parsedExpression) { - if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { - oneTime = true; - exp = exp.substring(2); - } - var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; - var lexer = new Lexer(parseOptions); - var parser = new Parser(lexer, $filter, parseOptions); - parsedExpression = parser.parse(exp); - if (parsedExpression.constant) { - parsedExpression.$$watchDelegate = constantWatchDelegate; - } else if (oneTime) { - parsedExpression.$$watchDelegate = parsedExpression.literal ? - oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; - } else if (parsedExpression.inputs) { - parsedExpression.$$watchDelegate = inputsWatchDelegate; - } - cache[cacheKey] = parsedExpression; - } - return addInterceptor(parsedExpression, interceptorFn); - - case 'function': - return addInterceptor(exp, interceptorFn); - - default: - return noop; - } - }; - - function expressionInputDirtyCheck(newValue, oldValueOfValue) { - - if (newValue == null || oldValueOfValue == null) { // null/undefined - return newValue === oldValueOfValue; - } - - if (typeof newValue === 'object') { - - // attempt to convert the value to a primitive type - // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can - // be cheaply dirty-checked - newValue = getValueOf(newValue); - - if (typeof newValue === 'object') { - // objects/arrays are not supported - deep-watching them would be too expensive - return false; - } - - // fall-through to the primitive equality check - } - - //Primitive or NaN - return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); - } - - function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { - var inputExpressions = parsedExpression.inputs; - var lastResult; - - if (inputExpressions.length === 1) { - var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails - inputExpressions = inputExpressions[0]; - return scope.$watch(function expressionInputWatch(scope) { - var newInputValue = inputExpressions(scope); - if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) { - lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); - oldInputValueOf = newInputValue && getValueOf(newInputValue); - } - return lastResult; - }, listener, objectEquality, prettyPrintExpression); - } - - var oldInputValueOfValues = []; - var oldInputValues = []; - for (var i = 0, ii = inputExpressions.length; i < ii; i++) { - oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails - oldInputValues[i] = null; - } - - return scope.$watch(function expressionInputsWatch(scope) { - var changed = false; - - for (var i = 0, ii = inputExpressions.length; i < ii; i++) { - var newInputValue = inputExpressions[i](scope); - if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { - oldInputValues[i] = newInputValue; - oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); - } - } - - if (changed) { - lastResult = parsedExpression(scope, undefined, undefined, oldInputValues); - } - - return lastResult; - }, listener, objectEquality, prettyPrintExpression); - } - - function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch, lastValue; - return unwatch = scope.$watch(function oneTimeWatch(scope) { - return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { - lastValue = value; - if (isFunction(listener)) { - listener.apply(this, arguments); - } - if (isDefined(value)) { - scope.$$postDigest(function() { - if (isDefined(lastValue)) { - unwatch(); - } - }); - } - }, objectEquality); - } - - function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch, lastValue; - return unwatch = scope.$watch(function oneTimeWatch(scope) { - return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { - lastValue = value; - if (isFunction(listener)) { - listener.call(this, value, old, scope); - } - if (isAllDefined(value)) { - scope.$$postDigest(function() { - if (isAllDefined(lastValue)) unwatch(); - }); - } - }, objectEquality); - - function isAllDefined(value) { - var allDefined = true; - forEach(value, function(val) { - if (!isDefined(val)) allDefined = false; - }); - return allDefined; - } - } - - function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch; - return unwatch = scope.$watch(function constantWatch(scope) { - return parsedExpression(scope); - }, function constantListener(value, old, scope) { - if (isFunction(listener)) { - listener.apply(this, arguments); - } - unwatch(); - }, objectEquality); - } - - function addInterceptor(parsedExpression, interceptorFn) { - if (!interceptorFn) return parsedExpression; - var watchDelegate = parsedExpression.$$watchDelegate; - - var regularWatch = - watchDelegate !== oneTimeLiteralWatchDelegate && - watchDelegate !== oneTimeWatchDelegate; - - var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); - return interceptorFn(value, scope, locals); - } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); - var result = interceptorFn(value, scope, locals); - // we only return the interceptor's result if the - // initial value is defined (for bind-once) - return isDefined(value) ? result : value; - }; - - // Propagate $$watchDelegates other then inputsWatchDelegate - if (parsedExpression.$$watchDelegate && - parsedExpression.$$watchDelegate !== inputsWatchDelegate) { - fn.$$watchDelegate = parsedExpression.$$watchDelegate; - } else if (!interceptorFn.$stateful) { - // If there is an interceptor, but no watchDelegate then treat the interceptor like - // we treat filters - it is assumed to be a pure function unless flagged with $stateful - fn.$$watchDelegate = inputsWatchDelegate; - fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; - } - - return fn; - } - }]; -} - -/** - * @ngdoc service - * @name $q - * @requires $rootScope - * - * @description - * A service that helps you run functions asynchronously, and use their return values (or exceptions) - * when they are done processing. - * - * This is an implementation of promises/deferred objects inspired by - * [Kris Kowal's Q](https://github.com/kriskowal/q). - * - * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred - * implementations, and the other which resembles ES6 promises to some degree. - * - * # $q constructor - * - * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` - * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony, - * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). - * - * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are - * available yet. - * - * It can be used like so: - * - * ```js - * // for the purpose of this example let's assume that variables `$q` and `okToGreet` - * // are available in the current lexical scope (they could have been injected or passed in). - * - * function asyncGreet(name) { - * // perform some asynchronous operation, resolve or reject the promise when appropriate. - * return $q(function(resolve, reject) { - * setTimeout(function() { - * if (okToGreet(name)) { - * resolve('Hello, ' + name + '!'); - * } else { - * reject('Greeting ' + name + ' is not allowed.'); - * } - * }, 1000); - * }); - * } - * - * var promise = asyncGreet('Robin Hood'); - * promise.then(function(greeting) { - * alert('Success: ' + greeting); - * }, function(reason) { - * alert('Failed: ' + reason); - * }); - * ``` - * - * Note: progress/notify callbacks are not currently supported via the ES6-style interface. - * - * However, the more traditional CommonJS-style usage is still available, and documented below. - * - * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an - * interface for interacting with an object that represents the result of an action that is - * performed asynchronously, and may or may not be finished at any given point in time. - * - * From the perspective of dealing with error handling, deferred and promise APIs are to - * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. - * - * ```js - * // for the purpose of this example let's assume that variables `$q` and `okToGreet` - * // are available in the current lexical scope (they could have been injected or passed in). - * - * function asyncGreet(name) { - * var deferred = $q.defer(); - * - * setTimeout(function() { - * deferred.notify('About to greet ' + name + '.'); - * - * if (okToGreet(name)) { - * deferred.resolve('Hello, ' + name + '!'); - * } else { - * deferred.reject('Greeting ' + name + ' is not allowed.'); - * } - * }, 1000); - * - * return deferred.promise; - * } - * - * var promise = asyncGreet('Robin Hood'); - * promise.then(function(greeting) { - * alert('Success: ' + greeting); - * }, function(reason) { - * alert('Failed: ' + reason); - * }, function(update) { - * alert('Got notification: ' + update); - * }); - * ``` - * - * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of guarantees that promise and deferred APIs make, see - * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. - * - * Additionally the promise api allows for composition that is very hard to do with the - * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. - * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the - * section on serial or parallel joining of promises. - * - * # The Deferred API - * - * A new instance of deferred is constructed by calling `$q.defer()`. - * - * The purpose of the deferred object is to expose the associated Promise instance as well as APIs - * that can be used for signaling the successful or unsuccessful completion, as well as the status - * of the task. - * - * **Methods** - * - * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection - * constructed via `$q.reject`, the promise will be rejected instead. - * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to - * resolving it with a rejection constructed via `$q.reject`. - * - `notify(value)` - provides updates on the status of the promise's execution. This may be called - * multiple times before the promise is either resolved or rejected. - * - * **Properties** - * - * - promise – `{Promise}` – promise object associated with this deferred. - * - * - * # The Promise API - * - * A new promise instance is created when a deferred instance is created and can be retrieved by - * calling `deferred.promise`. - * - * The purpose of the promise object is to allow for interested parties to get access to the result - * of the deferred task when it completes. - * - * **Methods** - * - * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or - * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously - * as soon as the result is available. The callbacks are called with a single argument: the result - * or rejection reason. Additionally, the notify callback may be called zero or more times to - * provide a progress indication, before the promise is resolved or rejected. - * - * This method *returns a new promise* which is resolved or rejected via the return value of the - * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved - * with the value which is resolved in that promise using - * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)). - * It also notifies via the return value of the `notifyCallback` method. The promise cannot be - * resolved or rejected from the notifyCallback method. - * - * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` - * - * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, - * but to do so without modifying the final value. This is useful to release resources or do some - * clean-up that needs to be done whether the promise was rejected or resolved. See the [full - * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for - * more information. - * - * # Chaining promises - * - * Because calling the `then` method of a promise returns a new derived promise, it is easily - * possible to create a chain of promises: - * - * ```js - * promiseB = promiseA.then(function(result) { - * return result + 1; - * }); - * - * // promiseB will be resolved immediately after promiseA is resolved and its value - * // will be the result of promiseA incremented by 1 - * ``` - * - * It is possible to create chains of any length and since a promise can be resolved with another - * promise (which will defer its resolution further), it is possible to pause/defer resolution of - * the promises at any point in the chain. This makes it possible to implement powerful APIs like - * $http's response interceptors. - * - * - * # Differences between Kris Kowal's Q and $q - * - * There are two main differences: - * - * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation - * mechanism in angular, which means faster propagation of resolution or rejection into your - * models and avoiding unnecessary browser repaints, which would result in flickering UI. - * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains - * all the important functionality needed for common async tasks. - * - * # Testing - * - * ```js - * it('should simulate promise', inject(function($q, $rootScope) { - * var deferred = $q.defer(); - * var promise = deferred.promise; - * var resolvedValue; - * - * promise.then(function(value) { resolvedValue = value; }); - * expect(resolvedValue).toBeUndefined(); - * - * // Simulate resolving of promise - * deferred.resolve(123); - * // Note that the 'then' function does not get called synchronously. - * // This is because we want the promise API to always be async, whether or not - * // it got called synchronously or asynchronously. - * expect(resolvedValue).toBeUndefined(); - * - * // Propagate promise resolution to 'then' functions using $apply(). - * $rootScope.$apply(); - * expect(resolvedValue).toEqual(123); - * })); - * ``` - * - * @param {function(function, function)} resolver Function which is responsible for resolving or - * rejecting the newly created promise. The first parameter is a function which resolves the - * promise, the second parameter is a function which rejects the promise. - * - * @returns {Promise} The newly created promise. - */ -function $QProvider() { - - this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { - return qFactory(function(callback) { - $rootScope.$evalAsync(callback); - }, $exceptionHandler); - }]; -} - -function $$QProvider() { - this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { - return qFactory(function(callback) { - $browser.defer(callback); - }, $exceptionHandler); - }]; -} - -/** - * Constructs a promise manager. - * - * @param {function(function)} nextTick Function for executing functions in the next turn. - * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for - * debugging purposes. - * @returns {object} Promise manager. - */ -function qFactory(nextTick, exceptionHandler) { - var $qMinErr = minErr('$q', TypeError); - function callOnce(self, resolveFn, rejectFn) { - var called = false; - function wrap(fn) { - return function(value) { - if (called) return; - called = true; - fn.call(self, value); - }; - } - - return [wrap(resolveFn), wrap(rejectFn)]; - } - - /** - * @ngdoc method - * @name ng.$q#defer - * @kind function - * - * @description - * Creates a `Deferred` object which represents a task which will finish in the future. - * - * @returns {Deferred} Returns a new instance of deferred. - */ - var defer = function() { - return new Deferred(); - }; - - function Promise() { - this.$$state = { status: 0 }; - } - - extend(Promise.prototype, { - then: function(onFulfilled, onRejected, progressBack) { - if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { - return this; - } - var result = new Deferred(); - - this.$$state.pending = this.$$state.pending || []; - this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); - if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); - - return result.promise; - }, - - "catch": function(callback) { - return this.then(null, callback); - }, - - "finally": function(callback, progressBack) { - return this.then(function(value) { - return handleCallback(value, true, callback); - }, function(error) { - return handleCallback(error, false, callback); - }, progressBack); - } - }); - - //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native - function simpleBind(context, fn) { - return function(value) { - fn.call(context, value); - }; - } - - function processQueue(state) { - var fn, deferred, pending; - - pending = state.pending; - state.processScheduled = false; - state.pending = undefined; - for (var i = 0, ii = pending.length; i < ii; ++i) { - deferred = pending[i][0]; - fn = pending[i][state.status]; - try { - if (isFunction(fn)) { - deferred.resolve(fn(state.value)); - } else if (state.status === 1) { - deferred.resolve(state.value); - } else { - deferred.reject(state.value); - } - } catch (e) { - deferred.reject(e); - exceptionHandler(e); - } - } - } - - function scheduleProcessQueue(state) { - if (state.processScheduled || !state.pending) return; - state.processScheduled = true; - nextTick(function() { processQueue(state); }); - } - - function Deferred() { - this.promise = new Promise(); - //Necessary to support unbound execution :/ - this.resolve = simpleBind(this, this.resolve); - this.reject = simpleBind(this, this.reject); - this.notify = simpleBind(this, this.notify); - } - - extend(Deferred.prototype, { - resolve: function(val) { - if (this.promise.$$state.status) return; - if (val === this.promise) { - this.$$reject($qMinErr( - 'qcycle', - "Expected promise to be resolved with value other than itself '{0}'", - val)); - } else { - this.$$resolve(val); - } - - }, - - $$resolve: function(val) { - var then, fns; - - fns = callOnce(this, this.$$resolve, this.$$reject); - try { - if ((isObject(val) || isFunction(val))) then = val && val.then; - if (isFunction(then)) { - this.promise.$$state.status = -1; - then.call(val, fns[0], fns[1], this.notify); - } else { - this.promise.$$state.value = val; - this.promise.$$state.status = 1; - scheduleProcessQueue(this.promise.$$state); - } - } catch (e) { - fns[1](e); - exceptionHandler(e); - } - }, - - reject: function(reason) { - if (this.promise.$$state.status) return; - this.$$reject(reason); - }, - - $$reject: function(reason) { - this.promise.$$state.value = reason; - this.promise.$$state.status = 2; - scheduleProcessQueue(this.promise.$$state); - }, - - notify: function(progress) { - var callbacks = this.promise.$$state.pending; - - if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) { - nextTick(function() { - var callback, result; - for (var i = 0, ii = callbacks.length; i < ii; i++) { - result = callbacks[i][0]; - callback = callbacks[i][3]; - try { - result.notify(isFunction(callback) ? callback(progress) : progress); - } catch (e) { - exceptionHandler(e); - } - } - }); - } - } - }); - - /** - * @ngdoc method - * @name $q#reject - * @kind function - * - * @description - * Creates a promise that is resolved as rejected with the specified `reason`. This api should be - * used to forward rejection in a chain of promises. If you are dealing with the last promise in - * a promise chain, you don't need to worry about it. - * - * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of - * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via - * a promise error callback and you want to forward the error to the promise derived from the - * current promise, you have to "rethrow" the error by returning a rejection constructed via - * `reject`. - * - * ```js - * promiseB = promiseA.then(function(result) { - * // success: do something and resolve promiseB - * // with the old or a new result - * return result; - * }, function(reason) { - * // error: handle the error if possible and - * // resolve promiseB with newPromiseOrValue, - * // otherwise forward the rejection to promiseB - * if (canHandle(reason)) { - * // handle the error and recover - * return newPromiseOrValue; - * } - * return $q.reject(reason); - * }); - * ``` - * - * @param {*} reason Constant, message, exception or an object representing the rejection reason. - * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. - */ - var reject = function(reason) { - var result = new Deferred(); - result.reject(reason); - return result.promise; - }; - - var makePromise = function makePromise(value, resolved) { - var result = new Deferred(); - if (resolved) { - result.resolve(value); - } else { - result.reject(value); - } - return result.promise; - }; - - var handleCallback = function handleCallback(value, isResolved, callback) { - var callbackOutput = null; - try { - if (isFunction(callback)) callbackOutput = callback(); - } catch (e) { - return makePromise(e, false); - } - if (isPromiseLike(callbackOutput)) { - return callbackOutput.then(function() { - return makePromise(value, isResolved); - }, function(error) { - return makePromise(error, false); - }); - } else { - return makePromise(value, isResolved); - } - }; - - /** - * @ngdoc method - * @name $q#when - * @kind function - * - * @description - * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. - * This is useful when you are dealing with an object that might or might not be a promise, or if - * the promise comes from a source that can't be trusted. - * - * @param {*} value Value or a promise - * @param {Function=} successCallback - * @param {Function=} errorCallback - * @param {Function=} progressCallback - * @returns {Promise} Returns a promise of the passed value or promise - */ - - - var when = function(value, callback, errback, progressBack) { - var result = new Deferred(); - result.resolve(value); - return result.promise.then(callback, errback, progressBack); - }; - - /** - * @ngdoc method - * @name $q#resolve - * @kind function - * - * @description - * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6. - * - * @param {*} value Value or a promise - * @param {Function=} successCallback - * @param {Function=} errorCallback - * @param {Function=} progressCallback - * @returns {Promise} Returns a promise of the passed value or promise - */ - var resolve = when; - - /** - * @ngdoc method - * @name $q#all - * @kind function - * - * @description - * Combines multiple promises into a single promise that is resolved when all of the input - * promises are resolved. - * - * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. - * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, - * each value corresponding to the promise at the same index/key in the `promises` array/hash. - * If any of the promises is resolved with a rejection, this resulting promise will be rejected - * with the same rejection value. - */ - - function all(promises) { - var deferred = new Deferred(), - counter = 0, - results = isArray(promises) ? [] : {}; - - forEach(promises, function(promise, key) { - counter++; - when(promise).then(function(value) { - if (results.hasOwnProperty(key)) return; - results[key] = value; - if (!(--counter)) deferred.resolve(results); - }, function(reason) { - if (results.hasOwnProperty(key)) return; - deferred.reject(reason); - }); - }); - - if (counter === 0) { - deferred.resolve(results); - } - - return deferred.promise; - } - - var $Q = function Q(resolver) { - if (!isFunction(resolver)) { - throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); - } - - if (!(this instanceof Q)) { - // More useful when $Q is the Promise itself. - return new Q(resolver); - } - - var deferred = new Deferred(); - - function resolveFn(value) { - deferred.resolve(value); - } - - function rejectFn(reason) { - deferred.reject(reason); - } - - resolver(resolveFn, rejectFn); - - return deferred.promise; - }; - - $Q.defer = defer; - $Q.reject = reject; - $Q.when = when; - $Q.resolve = resolve; - $Q.all = all; - - return $Q; -} - -function $$RAFProvider() { //rAF - this.$get = ['$window', '$timeout', function($window, $timeout) { - var requestAnimationFrame = $window.requestAnimationFrame || - $window.webkitRequestAnimationFrame; - - var cancelAnimationFrame = $window.cancelAnimationFrame || - $window.webkitCancelAnimationFrame || - $window.webkitCancelRequestAnimationFrame; - - var rafSupported = !!requestAnimationFrame; - var raf = rafSupported - ? function(fn) { - var id = requestAnimationFrame(fn); - return function() { - cancelAnimationFrame(id); - }; - } - : function(fn) { - var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 - return function() { - $timeout.cancel(timer); - }; - }; - - raf.supported = rafSupported; - - return raf; - }]; -} - -/** - * DESIGN NOTES - * - * The design decisions behind the scope are heavily favored for speed and memory consumption. - * - * The typical use of scope is to watch the expressions, which most of the time return the same - * value as last time so we optimize the operation. - * - * Closures construction is expensive in terms of speed as well as memory: - * - No closures, instead use prototypical inheritance for API - * - Internal state needs to be stored on scope directly, which means that private state is - * exposed as $$____ properties - * - * Loop operations are optimized by using while(count--) { ... } - * - this means that in order to keep the same order of execution as addition we have to add - * items to the array at the beginning (unshift) instead of at the end (push) - * - * Child scopes are created and removed often - * - Using an array would be slow since inserts in middle are expensive so we use linked list - * - * There are few watches then a lot of observers. This is why you don't want the observer to be - * implemented in the same way as watch. Watch requires return of initialization function which - * are expensive to construct. - */ - - -/** - * @ngdoc provider - * @name $rootScopeProvider - * @description - * - * Provider for the $rootScope service. - */ - -/** - * @ngdoc method - * @name $rootScopeProvider#digestTtl - * @description - * - * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and - * assuming that the model is unstable. - * - * The current default is 10 iterations. - * - * In complex applications it's possible that the dependencies between `$watch`s will result in - * several digest iterations. However if an application needs more than the default 10 digest - * iterations for its model to stabilize then you should investigate what is causing the model to - * continuously change during the digest. - * - * Increasing the TTL could have performance implications, so you should not change it without - * proper justification. - * - * @param {number} limit The number of digest iterations. - */ - - -/** - * @ngdoc service - * @name $rootScope - * @description - * - * Every application has a single root {@link ng.$rootScope.Scope scope}. - * All other scopes are descendant scopes of the root scope. Scopes provide separation - * between the model and the view, via a mechanism for watching the model for changes. - * They also provide an event emission/broadcast and subscription facility. See the - * {@link guide/scope developer guide on scopes}. - */ -function $RootScopeProvider() { - var TTL = 10; - var $rootScopeMinErr = minErr('$rootScope'); - var lastDirtyWatch = null; - var applyAsyncId = null; - - this.digestTtl = function(value) { - if (arguments.length) { - TTL = value; - } - return TTL; - }; - - function createChildScopeClass(parent) { - function ChildScope() { - this.$$watchers = this.$$nextSibling = - this.$$childHead = this.$$childTail = null; - this.$$listeners = {}; - this.$$listenerCount = {}; - this.$$watchersCount = 0; - this.$id = nextUid(); - this.$$ChildScope = null; - } - ChildScope.prototype = parent; - return ChildScope; - } - - this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', - function($injector, $exceptionHandler, $parse, $browser) { - - function destroyChildScope($event) { - $event.currentScope.$$destroyed = true; - } - - /** - * @ngdoc type - * @name $rootScope.Scope - * - * @description - * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the - * {@link auto.$injector $injector}. Child scopes are created using the - * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when - * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for - * an in-depth introduction and usage examples. - * - * - * # Inheritance - * A scope can inherit from a parent scope, as in this example: - * ```js - var parent = $rootScope; - var child = parent.$new(); - - parent.salutation = "Hello"; - expect(child.salutation).toEqual('Hello'); - - child.salutation = "Welcome"; - expect(child.salutation).toEqual('Welcome'); - expect(parent.salutation).toEqual('Hello'); - * ``` - * - * When interacting with `Scope` in tests, additional helper methods are available on the - * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional - * details. - * - * - * @param {Object.<string, function()>=} providers Map of service factory which need to be - * provided for the current scope. Defaults to {@link ng}. - * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy - * when unit-testing and having the need to override a default - * service. - * @returns {Object} Newly created scope. - * - */ - function Scope() { - this.$id = nextUid(); - this.$$phase = this.$parent = this.$$watchers = - this.$$nextSibling = this.$$prevSibling = - this.$$childHead = this.$$childTail = null; - this.$root = this; - this.$$destroyed = false; - this.$$listeners = {}; - this.$$listenerCount = {}; - this.$$watchersCount = 0; - this.$$isolateBindings = null; - } - - /** - * @ngdoc property - * @name $rootScope.Scope#$id - * - * @description - * Unique scope ID (monotonically increasing) useful for debugging. - */ - - /** - * @ngdoc property - * @name $rootScope.Scope#$parent - * - * @description - * Reference to the parent scope. - */ - - /** - * @ngdoc property - * @name $rootScope.Scope#$root - * - * @description - * Reference to the root scope. - */ - - Scope.prototype = { - constructor: Scope, - /** - * @ngdoc method - * @name $rootScope.Scope#$new - * @kind function - * - * @description - * Creates a new child {@link ng.$rootScope.Scope scope}. - * - * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. - * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. - * - * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is - * desired for the scope and its child scopes to be permanently detached from the parent and - * thus stop participating in model change detection and listener notification by invoking. - * - * @param {boolean} isolate If true, then the scope does not prototypically inherit from the - * parent scope. The scope is isolated, as it can not see parent scope properties. - * When creating widgets, it is useful for the widget to not accidentally read parent - * state. - * - * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` - * of the newly created scope. Defaults to `this` scope if not provided. - * This is used when creating a transclude scope to correctly place it - * in the scope hierarchy while maintaining the correct prototypical - * inheritance. - * - * @returns {Object} The newly created child scope. - * - */ - $new: function(isolate, parent) { - var child; - - parent = parent || this; - - if (isolate) { - child = new Scope(); - child.$root = this.$root; - } else { - // Only create a child scope class if somebody asks for one, - // but cache it to allow the VM to optimize lookups. - if (!this.$$ChildScope) { - this.$$ChildScope = createChildScopeClass(this); - } - child = new this.$$ChildScope(); - } - child.$parent = parent; - child.$$prevSibling = parent.$$childTail; - if (parent.$$childHead) { - parent.$$childTail.$$nextSibling = child; - parent.$$childTail = child; - } else { - parent.$$childHead = parent.$$childTail = child; - } - - // When the new scope is not isolated or we inherit from `this`, and - // the parent scope is destroyed, the property `$$destroyed` is inherited - // prototypically. In all other cases, this property needs to be set - // when the parent scope is destroyed. - // The listener needs to be added after the parent is set - if (isolate || parent != this) child.$on('$destroy', destroyChildScope); - - return child; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$watch - * @kind function - * - * @description - * Registers a `listener` callback to be executed whenever the `watchExpression` changes. - * - * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest - * $digest()} and should return the value that will be watched. (`watchExpression` should not change - * its value when executed multiple times with the same input because it may be executed multiple - * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be - * [idempotent](http://en.wikipedia.org/wiki/Idempotence). - * - The `listener` is called only when the value from the current `watchExpression` and the - * previous call to `watchExpression` are not equal (with the exception of the initial run, - * see below). Inequality is determined according to reference inequality, - * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) - * via the `!==` Javascript operator, unless `objectEquality == true` - * (see next point) - * - When `objectEquality == true`, inequality of the `watchExpression` is determined - * according to the {@link angular.equals} function. To save the value of the object for - * later comparison, the {@link angular.copy} function is used. This therefore means that - * watching complex objects will have adverse memory and performance implications. - * - The watch `listener` may change the model, which may trigger other `listener`s to fire. - * This is achieved by rerunning the watchers until no changes are detected. The rerun - * iteration limit is 10 to prevent an infinite loop deadlock. - * - * - * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, - * you can register a `watchExpression` function with no `listener`. (Be prepared for - * multiple calls to your `watchExpression` because it will execute multiple times in a - * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.) - * - * After a watcher is registered with the scope, the `listener` fn is called asynchronously - * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the - * watcher. In rare cases, this is undesirable because the listener is called when the result - * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you - * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the - * listener was called due to initialization. - * - * - * - * # Example - * ```js - // let's assume that scope was dependency injected as the $rootScope - var scope = $rootScope; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { - scope.counter = scope.counter + 1; - }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // the listener is always called during the first $digest loop after it was registered - expect(scope.counter).toEqual(1); - - scope.$digest(); - // but now it will not be called unless the value changes - expect(scope.counter).toEqual(1); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(2); - - - - // Using a function as a watchExpression - var food; - scope.foodCounter = 0; - expect(scope.foodCounter).toEqual(0); - scope.$watch( - // This function returns the value being watched. It is called for each turn of the $digest loop - function() { return food; }, - // This is the change listener, called when the value returned from the above function changes - function(newValue, oldValue) { - if ( newValue !== oldValue ) { - // Only increment the counter if the value changed - scope.foodCounter = scope.foodCounter + 1; - } - } - ); - // No digest has been run so the counter will be zero - expect(scope.foodCounter).toEqual(0); - - // Run the digest but since food has not changed count will still be zero - scope.$digest(); - expect(scope.foodCounter).toEqual(0); - - // Update food and run digest. Now the counter will increment - food = 'cheeseburger'; - scope.$digest(); - expect(scope.foodCounter).toEqual(1); - - * ``` - * - * - * - * @param {(function()|string)} watchExpression Expression that is evaluated on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers - * a call to the `listener`. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(scope)`: called with current `scope` as a parameter. - * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value - * of `watchExpression` changes. - * - * - `newVal` contains the current value of the `watchExpression` - * - `oldVal` contains the previous value of the `watchExpression` - * - `scope` refers to the current scope - * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of - * comparing for reference equality. - * @returns {function()} Returns a deregistration function for this listener. - */ - $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) { - var get = $parse(watchExp); - - if (get.$$watchDelegate) { - return get.$$watchDelegate(this, listener, objectEquality, get, watchExp); - } - var scope = this, - array = scope.$$watchers, - watcher = { - fn: listener, - last: initWatchVal, - get: get, - exp: prettyPrintExpression || watchExp, - eq: !!objectEquality - }; - - lastDirtyWatch = null; - - if (!isFunction(listener)) { - watcher.fn = noop; - } - - if (!array) { - array = scope.$$watchers = []; - } - // we use unshift since we use a while loop in $digest for speed. - // the while loop reads in reverse order. - array.unshift(watcher); - incrementWatchersCount(this, 1); - - return function deregisterWatch() { - if (arrayRemove(array, watcher) >= 0) { - incrementWatchersCount(scope, -1); - } - lastDirtyWatch = null; - }; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$watchGroup - * @kind function - * - * @description - * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. - * If any one expression in the collection changes the `listener` is executed. - * - * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every - * call to $digest() to see if any items changes. - * - The `listener` is called whenever any expression in the `watchExpressions` array changes. - * - * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually - * watched using {@link ng.$rootScope.Scope#$watch $watch()} - * - * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any - * expression in `watchExpressions` changes - * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching - * those of `watchExpression` - * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching - * those of `watchExpression` - * The `scope` refers to the current scope. - * @returns {function()} Returns a de-registration function for all listeners. - */ - $watchGroup: function(watchExpressions, listener) { - var oldValues = new Array(watchExpressions.length); - var newValues = new Array(watchExpressions.length); - var deregisterFns = []; - var self = this; - var changeReactionScheduled = false; - var firstRun = true; - - if (!watchExpressions.length) { - // No expressions means we call the listener ASAP - var shouldCall = true; - self.$evalAsync(function() { - if (shouldCall) listener(newValues, newValues, self); - }); - return function deregisterWatchGroup() { - shouldCall = false; - }; - } - - if (watchExpressions.length === 1) { - // Special case size of one - return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { - newValues[0] = value; - oldValues[0] = oldValue; - listener(newValues, (value === oldValue) ? newValues : oldValues, scope); - }); - } - - forEach(watchExpressions, function(expr, i) { - var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { - newValues[i] = value; - oldValues[i] = oldValue; - if (!changeReactionScheduled) { - changeReactionScheduled = true; - self.$evalAsync(watchGroupAction); - } - }); - deregisterFns.push(unwatchFn); - }); - - function watchGroupAction() { - changeReactionScheduled = false; - - if (firstRun) { - firstRun = false; - listener(newValues, newValues, self); - } else { - listener(newValues, oldValues, self); - } - } - - return function deregisterWatchGroup() { - while (deregisterFns.length) { - deregisterFns.shift()(); - } - }; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$watchCollection - * @kind function - * - * @description - * Shallow watches the properties of an object and fires whenever any of the properties change - * (for arrays, this implies watching the array items; for object maps, this implies watching - * the properties). If a change is detected, the `listener` callback is fired. - * - * - The `obj` collection is observed via standard $watch operation and is examined on every - * call to $digest() to see if any items have been added, removed, or moved. - * - The `listener` is called whenever anything within the `obj` has changed. Examples include - * adding, removing, and moving items belonging to an object or array. - * - * - * # Example - * ```js - $scope.names = ['igor', 'matias', 'misko', 'james']; - $scope.dataCount = 4; - - $scope.$watchCollection('names', function(newNames, oldNames) { - $scope.dataCount = newNames.length; - }); - - expect($scope.dataCount).toEqual(4); - $scope.$digest(); - - //still at 4 ... no changes - expect($scope.dataCount).toEqual(4); - - $scope.names.pop(); - $scope.$digest(); - - //now there's been a change - expect($scope.dataCount).toEqual(3); - * ``` - * - * - * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The - * expression value should evaluate to an object or an array which is observed on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the - * collection will trigger a call to the `listener`. - * - * @param {function(newCollection, oldCollection, scope)} listener a callback function called - * when a change is detected. - * - The `newCollection` object is the newly modified data obtained from the `obj` expression - * - The `oldCollection` object is a copy of the former collection data. - * Due to performance considerations, the`oldCollection` value is computed only if the - * `listener` function declares two or more arguments. - * - The `scope` argument refers to the current scope. - * - * @returns {function()} Returns a de-registration function for this listener. When the - * de-registration function is executed, the internal watch operation is terminated. - */ - $watchCollection: function(obj, listener) { - $watchCollectionInterceptor.$stateful = true; - - var self = this; - // the current value, updated on each dirty-check run - var newValue; - // a shallow copy of the newValue from the last dirty-check run, - // updated to match newValue during dirty-check run - var oldValue; - // a shallow copy of the newValue from when the last change happened - var veryOldValue; - // only track veryOldValue if the listener is asking for it - var trackVeryOldValue = (listener.length > 1); - var changeDetected = 0; - var changeDetector = $parse(obj, $watchCollectionInterceptor); - var internalArray = []; - var internalObject = {}; - var initRun = true; - var oldLength = 0; - - function $watchCollectionInterceptor(_value) { - newValue = _value; - var newLength, key, bothNaN, newItem, oldItem; - - // If the new value is undefined, then return undefined as the watch may be a one-time watch - if (isUndefined(newValue)) return; - - if (!isObject(newValue)) { // if primitive - if (oldValue !== newValue) { - oldValue = newValue; - changeDetected++; - } - } else if (isArrayLike(newValue)) { - if (oldValue !== internalArray) { - // we are transitioning from something which was not an array into array. - oldValue = internalArray; - oldLength = oldValue.length = 0; - changeDetected++; - } - - newLength = newValue.length; - - if (oldLength !== newLength) { - // if lengths do not match we need to trigger change notification - changeDetected++; - oldValue.length = oldLength = newLength; - } - // copy the items to oldValue and look for changes. - for (var i = 0; i < newLength; i++) { - oldItem = oldValue[i]; - newItem = newValue[i]; - - bothNaN = (oldItem !== oldItem) && (newItem !== newItem); - if (!bothNaN && (oldItem !== newItem)) { - changeDetected++; - oldValue[i] = newItem; - } - } - } else { - if (oldValue !== internalObject) { - // we are transitioning from something which was not an object into object. - oldValue = internalObject = {}; - oldLength = 0; - changeDetected++; - } - // copy the items to oldValue and look for changes. - newLength = 0; - for (key in newValue) { - if (hasOwnProperty.call(newValue, key)) { - newLength++; - newItem = newValue[key]; - oldItem = oldValue[key]; - - if (key in oldValue) { - bothNaN = (oldItem !== oldItem) && (newItem !== newItem); - if (!bothNaN && (oldItem !== newItem)) { - changeDetected++; - oldValue[key] = newItem; - } - } else { - oldLength++; - oldValue[key] = newItem; - changeDetected++; - } - } - } - if (oldLength > newLength) { - // we used to have more keys, need to find them and destroy them. - changeDetected++; - for (key in oldValue) { - if (!hasOwnProperty.call(newValue, key)) { - oldLength--; - delete oldValue[key]; - } - } - } - } - return changeDetected; - } - - function $watchCollectionAction() { - if (initRun) { - initRun = false; - listener(newValue, newValue, self); - } else { - listener(newValue, veryOldValue, self); - } - - // make a copy for the next time a collection is changed - if (trackVeryOldValue) { - if (!isObject(newValue)) { - //primitive - veryOldValue = newValue; - } else if (isArrayLike(newValue)) { - veryOldValue = new Array(newValue.length); - for (var i = 0; i < newValue.length; i++) { - veryOldValue[i] = newValue[i]; - } - } else { // if object - veryOldValue = {}; - for (var key in newValue) { - if (hasOwnProperty.call(newValue, key)) { - veryOldValue[key] = newValue[key]; - } - } - } - } - } - - return this.$watch(changeDetector, $watchCollectionAction); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$digest - * @kind function - * - * @description - * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and - * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change - * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} - * until no more listeners are firing. This means that it is possible to get into an infinite - * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of - * iterations exceeds 10. - * - * Usually, you don't call `$digest()` directly in - * {@link ng.directive:ngController controllers} or in - * {@link ng.$compileProvider#directive directives}. - * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within - * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. - * - * If you want to be notified whenever `$digest()` is called, - * you can register a `watchExpression` function with - * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. - * - * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. - * - * # Example - * ```js - var scope = ...; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { - scope.counter = scope.counter + 1; - }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // the listener is always called during the first $digest loop after it was registered - expect(scope.counter).toEqual(1); - - scope.$digest(); - // but now it will not be called unless the value changes - expect(scope.counter).toEqual(1); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(2); - * ``` - * - */ - $digest: function() { - var watch, value, last, - watchers, - length, - dirty, ttl = TTL, - next, current, target = this, - watchLog = [], - logIdx, logMsg, asyncTask; - - beginPhase('$digest'); - // Check for changes to browser url that happened in sync before the call to $digest - $browser.$$checkUrlChange(); - - if (this === $rootScope && applyAsyncId !== null) { - // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then - // cancel the scheduled $apply and flush the queue of expressions to be evaluated. - $browser.defer.cancel(applyAsyncId); - flushApplyAsync(); - } - - lastDirtyWatch = null; - - do { // "while dirty" loop - dirty = false; - current = target; - - while (asyncQueue.length) { - try { - asyncTask = asyncQueue.shift(); - asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); - } catch (e) { - $exceptionHandler(e); - } - lastDirtyWatch = null; - } - - traverseScopesLoop: - do { // "traverse the scopes" loop - if ((watchers = current.$$watchers)) { - // process our watches - length = watchers.length; - while (length--) { - try { - watch = watchers[length]; - // Most common watches are on primitives, in which case we can short - // circuit it with === operator, only when === fails do we use .equals - if (watch) { - if ((value = watch.get(current)) !== (last = watch.last) && - !(watch.eq - ? equals(value, last) - : (typeof value === 'number' && typeof last === 'number' - && isNaN(value) && isNaN(last)))) { - dirty = true; - lastDirtyWatch = watch; - watch.last = watch.eq ? copy(value, null) : value; - watch.fn(value, ((last === initWatchVal) ? value : last), current); - if (ttl < 5) { - logIdx = 4 - ttl; - if (!watchLog[logIdx]) watchLog[logIdx] = []; - watchLog[logIdx].push({ - msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, - newVal: value, - oldVal: last - }); - } - } else if (watch === lastDirtyWatch) { - // If the most recently dirty watcher is now clean, short circuit since the remaining watchers - // have already been tested. - dirty = false; - break traverseScopesLoop; - } - } - } catch (e) { - $exceptionHandler(e); - } - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $broadcast - if (!(next = ((current.$$watchersCount && current.$$childHead) || - (current !== target && current.$$nextSibling)))) { - while (current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } while ((current = next)); - - // `break traverseScopesLoop;` takes us to here - - if ((dirty || asyncQueue.length) && !(ttl--)) { - clearPhase(); - throw $rootScopeMinErr('infdig', - '{0} $digest() iterations reached. Aborting!\n' + - 'Watchers fired in the last 5 iterations: {1}', - TTL, watchLog); - } - - } while (dirty || asyncQueue.length); - - clearPhase(); - - while (postDigestQueue.length) { - try { - postDigestQueue.shift()(); - } catch (e) { - $exceptionHandler(e); - } - } - }, - - - /** - * @ngdoc event - * @name $rootScope.Scope#$destroy - * @eventType broadcast on scope being destroyed - * - * @description - * Broadcasted when a scope and its children are being destroyed. - * - * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to - * clean up DOM bindings before an element is removed from the DOM. - */ - - /** - * @ngdoc method - * @name $rootScope.Scope#$destroy - * @kind function - * - * @description - * Removes the current scope (and all of its children) from the parent scope. Removal implies - * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer - * propagate to the current scope and its children. Removal also implies that the current - * scope is eligible for garbage collection. - * - * The `$destroy()` is usually used by directives such as - * {@link ng.directive:ngRepeat ngRepeat} for managing the - * unrolling of the loop. - * - * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. - * Application code can register a `$destroy` event handler that will give it a chance to - * perform any necessary cleanup. - * - * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to - * clean up DOM bindings before an element is removed from the DOM. - */ - $destroy: function() { - // We can't destroy a scope that has been already destroyed. - if (this.$$destroyed) return; - var parent = this.$parent; - - this.$broadcast('$destroy'); - this.$$destroyed = true; - - if (this === $rootScope) { - //Remove handlers attached to window when $rootScope is removed - $browser.$$applicationDestroyed(); - } - - incrementWatchersCount(this, -this.$$watchersCount); - for (var eventName in this.$$listenerCount) { - decrementListenerCount(this, this.$$listenerCount[eventName], eventName); - } - - // sever all the references to parent scopes (after this cleanup, the current scope should - // not be retained by any of our references and should be eligible for garbage collection) - if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; - if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; - if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; - if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; - - // Disable listeners, watchers and apply/digest methods - this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; - this.$on = this.$watch = this.$watchGroup = function() { return noop; }; - this.$$listeners = {}; - - // All of the code below is bogus code that works around V8's memory leak via optimized code - // and inline caches. - // - // see: - // - https://code.google.com/p/v8/issues/detail?id=2073#c26 - // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 - // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 - - this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = - this.$$childTail = this.$root = this.$$watchers = null; - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$eval - * @kind function - * - * @description - * Executes the `expression` on the current scope and returns the result. Any exceptions in - * the expression are propagated (uncaught). This is useful when evaluating Angular - * expressions. - * - * # Example - * ```js - var scope = ng.$rootScope.Scope(); - scope.a = 1; - scope.b = 2; - - expect(scope.$eval('a+b')).toEqual(3); - expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); - * ``` - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @param {(object)=} locals Local variables object, useful for overriding values in scope. - * @returns {*} The result of evaluating the expression. - */ - $eval: function(expr, locals) { - return $parse(expr)(this, locals); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$evalAsync - * @kind function - * - * @description - * Executes the expression on the current scope at a later point in time. - * - * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only - * that: - * - * - it will execute after the function that scheduled the evaluation (preferably before DOM - * rendering). - * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after - * `expression` execution. - * - * Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle - * will be scheduled. However, it is encouraged to always call code that changes the model - * from within an `$apply` call. That includes code evaluated via `$evalAsync`. - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @param {(object)=} locals Local variables object, useful for overriding values in scope. - */ - $evalAsync: function(expr, locals) { - // if we are outside of an $digest loop and this is the first time we are scheduling async - // task also schedule async auto-flush - if (!$rootScope.$$phase && !asyncQueue.length) { - $browser.defer(function() { - if (asyncQueue.length) { - $rootScope.$digest(); - } - }); - } - - asyncQueue.push({scope: this, expression: expr, locals: locals}); - }, - - $$postDigest: function(fn) { - postDigestQueue.push(fn); - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$apply - * @kind function - * - * @description - * `$apply()` is used to execute an expression in angular from outside of the angular - * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). - * Because we are calling into the angular framework we need to perform proper scope life - * cycle of {@link ng.$exceptionHandler exception handling}, - * {@link ng.$rootScope.Scope#$digest executing watches}. - * - * ## Life cycle - * - * # Pseudo-Code of `$apply()` - * ```js - function $apply(expr) { - try { - return $eval(expr); - } catch (e) { - $exceptionHandler(e); - } finally { - $root.$digest(); - } - } - * ``` - * - * - * Scope's `$apply()` method transitions through the following stages: - * - * 1. The {@link guide/expression expression} is executed using the - * {@link ng.$rootScope.Scope#$eval $eval()} method. - * 2. Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the - * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. - * - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $apply: function(expr) { - try { - beginPhase('$apply'); - try { - return this.$eval(expr); - } finally { - clearPhase(); - } - } catch (e) { - $exceptionHandler(e); - } finally { - try { - $rootScope.$digest(); - } catch (e) { - $exceptionHandler(e); - throw e; - } - } - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$applyAsync - * @kind function - * - * @description - * Schedule the invocation of $apply to occur at a later time. The actual time difference - * varies across browsers, but is typically around ~10 milliseconds. - * - * This can be used to queue up multiple expressions which need to be evaluated in the same - * digest. - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - */ - $applyAsync: function(expr) { - var scope = this; - expr && applyAsyncQueue.push($applyAsyncExpression); - scheduleApplyAsync(); - - function $applyAsyncExpression() { - scope.$eval(expr); - } - }, - - /** - * @ngdoc method - * @name $rootScope.Scope#$on - * @kind function - * - * @description - * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for - * discussion of event life cycle. - * - * The event listener function format is: `function(event, args...)`. The `event` object - * passed into the listener has the following attributes: - * - * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or - * `$broadcast`-ed. - * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the - * event propagates through the scope hierarchy, this property is set to null. - * - `name` - `{string}`: name of the event. - * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel - * further event propagation (available only for events that were `$emit`-ed). - * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag - * to true. - * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. - * - * @param {string} name Event name to listen on. - * @param {function(event, ...args)} listener Function to call when the event is emitted. - * @returns {function()} Returns a deregistration function for this listener. - */ - $on: function(name, listener) { - var namedListeners = this.$$listeners[name]; - if (!namedListeners) { - this.$$listeners[name] = namedListeners = []; - } - namedListeners.push(listener); - - var current = this; - do { - if (!current.$$listenerCount[name]) { - current.$$listenerCount[name] = 0; - } - current.$$listenerCount[name]++; - } while ((current = current.$parent)); - - var self = this; - return function() { - var indexOfListener = namedListeners.indexOf(listener); - if (indexOfListener !== -1) { - namedListeners[indexOfListener] = null; - decrementListenerCount(self, 1, name); - } - }; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$emit - * @kind function - * - * @description - * Dispatches an event `name` upwards through the scope hierarchy notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$emit` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get - * notified. Afterwards, the event traverses upwards toward the root scope and calls all - * registered listeners along the way. The event will stop propagating if one of the listeners - * cancels it. - * - * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to emit. - * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. - * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). - */ - $emit: function(name, args) { - var empty = [], - namedListeners, - scope = this, - stopPropagation = false, - event = { - name: name, - targetScope: scope, - stopPropagation: function() {stopPropagation = true;}, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }, - listenerArgs = concat([event], arguments, 1), - i, length; - - do { - namedListeners = scope.$$listeners[name] || empty; - event.currentScope = scope; - for (i = 0, length = namedListeners.length; i < length; i++) { - - // if listeners were deregistered, defragment the array - if (!namedListeners[i]) { - namedListeners.splice(i, 1); - i--; - length--; - continue; - } - try { - //allow all listeners attached to the current scope to run - namedListeners[i].apply(null, listenerArgs); - } catch (e) { - $exceptionHandler(e); - } - } - //if any listener on the current scope stops propagation, prevent bubbling - if (stopPropagation) { - event.currentScope = null; - return event; - } - //traverse upwards - scope = scope.$parent; - } while (scope); - - event.currentScope = null; - - return event; - }, - - - /** - * @ngdoc method - * @name $rootScope.Scope#$broadcast - * @kind function - * - * @description - * Dispatches an event `name` downwards to all child scopes (and their children) notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$broadcast` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get - * notified. Afterwards, the event propagates to all direct and indirect scopes of the current - * scope and calls all registered listeners along the way. The event cannot be canceled. - * - * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to broadcast. - * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} - */ - $broadcast: function(name, args) { - var target = this, - current = target, - next = target, - event = { - name: name, - targetScope: target, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }; - - if (!target.$$listenerCount[name]) return event; - - var listenerArgs = concat([event], arguments, 1), - listeners, i, length; - - //down while you can, then up and next sibling or up and next sibling until back at root - while ((current = next)) { - event.currentScope = current; - listeners = current.$$listeners[name] || []; - for (i = 0, length = listeners.length; i < length; i++) { - // if listeners were deregistered, defragment the array - if (!listeners[i]) { - listeners.splice(i, 1); - i--; - length--; - continue; - } - - try { - listeners[i].apply(null, listenerArgs); - } catch (e) { - $exceptionHandler(e); - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $digest - // (though it differs due to having the extra check for $$listenerCount) - if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || - (current !== target && current.$$nextSibling)))) { - while (current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } - - event.currentScope = null; - return event; - } - }; - - var $rootScope = new Scope(); - - //The internal queues. Expose them on the $rootScope for debugging/testing purposes. - var asyncQueue = $rootScope.$$asyncQueue = []; - var postDigestQueue = $rootScope.$$postDigestQueue = []; - var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; - - return $rootScope; - - - function beginPhase(phase) { - if ($rootScope.$$phase) { - throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); - } - - $rootScope.$$phase = phase; - } - - function clearPhase() { - $rootScope.$$phase = null; - } - - function incrementWatchersCount(current, count) { - do { - current.$$watchersCount += count; - } while ((current = current.$parent)); - } - - function decrementListenerCount(current, count, name) { - do { - current.$$listenerCount[name] -= count; - - if (current.$$listenerCount[name] === 0) { - delete current.$$listenerCount[name]; - } - } while ((current = current.$parent)); - } - - /** - * function used as an initial value for watchers. - * because it's unique we can easily tell it apart from other values - */ - function initWatchVal() {} - - function flushApplyAsync() { - while (applyAsyncQueue.length) { - try { - applyAsyncQueue.shift()(); - } catch (e) { - $exceptionHandler(e); - } - } - applyAsyncId = null; - } - - function scheduleApplyAsync() { - if (applyAsyncId === null) { - applyAsyncId = $browser.defer(function() { - $rootScope.$apply(flushApplyAsync); - }); - } - } - }]; -} - -/** - * @description - * Private service to sanitize uris for links and images. Used by $compile and $sanitize. - */ -function $$SanitizeUriProvider() { - var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, - imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; - - /** - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during a[href] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to a[href] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.aHrefSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - aHrefSanitizationWhitelist = regexp; - return this; - } - return aHrefSanitizationWhitelist; - }; - - - /** - * @description - * Retrieves or overrides the default regular expression that is used for whitelisting of safe - * urls during img[src] sanitization. - * - * The sanitization is a security measure aimed at prevent XSS attacks via html links. - * - * Any url about to be assigned to img[src] via data-binding is first normalized and turned into - * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` - * regular expression. If a match is found, the original url is written into the dom. Otherwise, - * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. - * - * @param {RegExp=} regexp New regexp to whitelist urls with. - * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for - * chaining otherwise. - */ - this.imgSrcSanitizationWhitelist = function(regexp) { - if (isDefined(regexp)) { - imgSrcSanitizationWhitelist = regexp; - return this; - } - return imgSrcSanitizationWhitelist; - }; - - this.$get = function() { - return function sanitizeUri(uri, isImage) { - var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; - var normalizedVal; - normalizedVal = urlResolve(uri).href; - if (normalizedVal !== '' && !normalizedVal.match(regex)) { - return 'unsafe:' + normalizedVal; - } - return uri; - }; - }; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Any commits to this file should be reviewed with security in mind. * - * Changes to this file can potentially create security vulnerabilities. * - * An approval from 2 Core members with history of modifying * - * this file is required. * - * * - * Does the change somehow allow for arbitrary javascript to be executed? * - * Or allows for someone to change the prototype of built-in objects? * - * Or gives undesired access to variables likes document or window? * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -var $sceMinErr = minErr('$sce'); - -var SCE_CONTEXTS = { - HTML: 'html', - CSS: 'css', - URL: 'url', - // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a - // url. (e.g. ng-include, script src, templateUrl) - RESOURCE_URL: 'resourceUrl', - JS: 'js' -}; - -// Helper functions follow. - -function adjustMatcher(matcher) { - if (matcher === 'self') { - return matcher; - } else if (isString(matcher)) { - // Strings match exactly except for 2 wildcards - '*' and '**'. - // '*' matches any character except those from the set ':/.?&'. - // '**' matches any character (like .* in a RegExp). - // More than 2 *'s raises an error as it's ill defined. - if (matcher.indexOf('***') > -1) { - throw $sceMinErr('iwcard', - 'Illegal sequence *** in string matcher. String: {0}', matcher); - } - matcher = escapeForRegexp(matcher). - replace('\\*\\*', '.*'). - replace('\\*', '[^:/.?&;]*'); - return new RegExp('^' + matcher + '$'); - } else if (isRegExp(matcher)) { - // The only other type of matcher allowed is a Regexp. - // Match entire URL / disallow partial matches. - // Flags are reset (i.e. no global, ignoreCase or multiline) - return new RegExp('^' + matcher.source + '$'); - } else { - throw $sceMinErr('imatcher', - 'Matchers may only be "self", string patterns or RegExp objects'); - } -} - - -function adjustMatchers(matchers) { - var adjustedMatchers = []; - if (isDefined(matchers)) { - forEach(matchers, function(matcher) { - adjustedMatchers.push(adjustMatcher(matcher)); - }); - } - return adjustedMatchers; -} - - -/** - * @ngdoc service - * @name $sceDelegate - * @kind function - * - * @description - * - * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict - * Contextual Escaping (SCE)} services to AngularJS. - * - * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of - * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is - * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to - * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things - * work because `$sce` delegates to `$sceDelegate` for these operations. - * - * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. - * - * The default instance of `$sceDelegate` should work out of the box with little pain. While you - * can override it completely to change the behavior of `$sce`, the common case would - * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting - * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as - * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist - * $sceDelegateProvider.resourceUrlWhitelist} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} - */ - -/** - * @ngdoc provider - * @name $sceDelegateProvider - * @description - * - * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate - * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure - * that the URLs used for sourcing Angular templates are safe. Refer {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and - * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} - * - * For the general details about this service in Angular, read the main page for {@link ng.$sce - * Strict Contextual Escaping (SCE)}. - * - * **Example**: Consider the following case. <a name="example"></a> - * - * - your app is hosted at url `http://myapp.example.com/` - * - but some of your templates are hosted on other domains you control such as - * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc. - * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. - * - * Here is what a secure configuration for this scenario might look like: - * - * ``` - * angular.module('myApp', []).config(function($sceDelegateProvider) { - * $sceDelegateProvider.resourceUrlWhitelist([ - * // Allow same origin resource loads. - * 'self', - * // Allow loading from our assets domain. Notice the difference between * and **. - * 'http://srv*.assets.example.com/**' - * ]); - * - * // The blacklist overrides the whitelist so the open redirect here is blocked. - * $sceDelegateProvider.resourceUrlBlacklist([ - * 'http://myapp.example.com/clickThru**' - * ]); - * }); - * ``` - */ - -function $SceDelegateProvider() { - this.SCE_CONTEXTS = SCE_CONTEXTS; - - // Resource URLs can also be trusted by policy. - var resourceUrlWhitelist = ['self'], - resourceUrlBlacklist = []; - - /** - * @ngdoc method - * @name $sceDelegateProvider#resourceUrlWhitelist - * @kind function - * - * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. - * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. - * - * Note: **an empty whitelist array will block all URLs**! - * - * @return {Array} the currently set whitelist array. - * - * The **default value** when no whitelist has been explicitly set is `['self']` allowing only - * same origin resource requests. - * - * @description - * Sets/Gets the whitelist of trusted resource URLs. - */ - this.resourceUrlWhitelist = function(value) { - if (arguments.length) { - resourceUrlWhitelist = adjustMatchers(value); - } - return resourceUrlWhitelist; - }; - - /** - * @ngdoc method - * @name $sceDelegateProvider#resourceUrlBlacklist - * @kind function - * - * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. - * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. - * - * The typical usage for the blacklist is to **block - * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as - * these would otherwise be trusted but actually return content from the redirected domain. - * - * Finally, **the blacklist overrides the whitelist** and has the final say. - * - * @return {Array} the currently set blacklist array. - * - * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there - * is no blacklist.) - * - * @description - * Sets/Gets the blacklist of trusted resource URLs. - */ - - this.resourceUrlBlacklist = function(value) { - if (arguments.length) { - resourceUrlBlacklist = adjustMatchers(value); - } - return resourceUrlBlacklist; - }; - - this.$get = ['$injector', function($injector) { - - var htmlSanitizer = function htmlSanitizer(html) { - throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); - }; - - if ($injector.has('$sanitize')) { - htmlSanitizer = $injector.get('$sanitize'); - } - - - function matchUrl(matcher, parsedUrl) { - if (matcher === 'self') { - return urlIsSameOrigin(parsedUrl); - } else { - // definitely a regex. See adjustMatchers() - return !!matcher.exec(parsedUrl.href); - } - } - - function isResourceUrlAllowedByPolicy(url) { - var parsedUrl = urlResolve(url.toString()); - var i, n, allowed = false; - // Ensure that at least one item from the whitelist allows this url. - for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { - if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { - allowed = true; - break; - } - } - if (allowed) { - // Ensure that no item from the blacklist blocked this url. - for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { - if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { - allowed = false; - break; - } - } - } - return allowed; - } - - function generateHolderType(Base) { - var holderType = function TrustedValueHolderType(trustedValue) { - this.$$unwrapTrustedValue = function() { - return trustedValue; - }; - }; - if (Base) { - holderType.prototype = new Base(); - } - holderType.prototype.valueOf = function sceValueOf() { - return this.$$unwrapTrustedValue(); - }; - holderType.prototype.toString = function sceToString() { - return this.$$unwrapTrustedValue().toString(); - }; - return holderType; - } - - var trustedValueHolderBase = generateHolderType(), - byType = {}; - - byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); - byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); - - /** - * @ngdoc method - * @name $sceDelegate#trustAs - * - * @description - * Returns an object that is trusted by angular for use in specified strict - * contextual escaping contexts (such as ng-bind-html, ng-include, any src - * attribute interpolation, any dom event binding attribute interpolation - * such as for onclick, etc.) that uses the provided value. - * See {@link ng.$sce $sce} for enabling strict contextual escaping. - * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. - */ - function trustAs(type, trustedValue) { - var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); - if (!Constructor) { - throw $sceMinErr('icontext', - 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', - type, trustedValue); - } - if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') { - return trustedValue; - } - // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting - // mutable objects, we ensure here that the value passed in is actually a string. - if (typeof trustedValue !== 'string') { - throw $sceMinErr('itype', - 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', - type); - } - return new Constructor(trustedValue); - } - - /** - * @ngdoc method - * @name $sceDelegate#valueOf - * - * @description - * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. - * - * If the passed parameter is not a value that had been returned by {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. - * - * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} - * call or anything else. - * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns - * `value` unchanged. - */ - function valueOf(maybeTrusted) { - if (maybeTrusted instanceof trustedValueHolderBase) { - return maybeTrusted.$$unwrapTrustedValue(); - } else { - return maybeTrusted; - } - } - - /** - * @ngdoc method - * @name $sceDelegate#getTrusted - * - * @description - * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and - * returns the originally supplied value if the queried context type is a supertype of the - * created type. If this condition isn't satisfied, throws an exception. - * - * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} call. - * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. - */ - function getTrusted(type, maybeTrusted) { - if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') { - return maybeTrusted; - } - var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); - if (constructor && maybeTrusted instanceof constructor) { - return maybeTrusted.$$unwrapTrustedValue(); - } - // If we get here, then we may only take one of two actions. - // 1. sanitize the value for the requested type, or - // 2. throw an exception. - if (type === SCE_CONTEXTS.RESOURCE_URL) { - if (isResourceUrlAllowedByPolicy(maybeTrusted)) { - return maybeTrusted; - } else { - throw $sceMinErr('insecurl', - 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', - maybeTrusted.toString()); - } - } else if (type === SCE_CONTEXTS.HTML) { - return htmlSanitizer(maybeTrusted); - } - throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); - } - - return { trustAs: trustAs, - getTrusted: getTrusted, - valueOf: valueOf }; - }]; -} - - -/** - * @ngdoc provider - * @name $sceProvider - * @description - * - * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. - * - enable/disable Strict Contextual Escaping (SCE) in a module - * - override the default implementation with a custom delegate - * - * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. - */ - -/* jshint maxlen: false*/ - -/** - * @ngdoc service - * @name $sce - * @kind function - * - * @description - * - * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. - * - * # Strict Contextual Escaping - * - * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain - * contexts to result in a value that is marked as safe to use for that context. One example of - * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer - * to these contexts as privileged or SCE contexts. - * - * As of version 1.2, Angular ships with SCE enabled by default. - * - * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow - * one to execute arbitrary javascript by the use of the expression() syntax. Refer - * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them. - * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` - * to the top of your HTML document. - * - * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for - * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. - * - * Here's an example of a binding in a privileged context: - * - * ``` - * <input ng-model="userHtml" aria-label="User input"> - * <div ng-bind-html="userHtml"></div> - * ``` - * - * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE - * disabled, this application allows the user to render arbitrary HTML into the DIV. - * In a more realistic example, one may be rendering user comments, blog articles, etc. via - * bindings. (HTML is just one example of a context where rendering user controlled input creates - * security vulnerabilities.) - * - * For the case of HTML, you might use a library, either on the client side, or on the server side, - * to sanitize unsafe HTML before binding to the value and rendering it in the document. - * - * How would you ensure that every place that used these types of bindings was bound to a value that - * was sanitized by your library (or returned as safe for rendering by your server?) How can you - * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some - * properties/fields and forgot to update the binding to the sanitized value? - * - * To be secure by default, you want to ensure that any such bindings are disallowed unless you can - * determine that something explicitly says it's safe to use a value for binding in that - * context. You can then audit your code (a simple grep would do) to ensure that this is only done - * for those values that you can easily tell are safe - because they were received from your server, - * sanitized by your library, etc. You can organize your codebase to help with this - perhaps - * allowing only the files in a specific directory to do this. Ensuring that the internal API - * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. - * - * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} - * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to - * obtain values that will be accepted by SCE / privileged contexts. - * - * - * ## How does it work? - * - * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted - * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link - * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the - * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. - * - * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link - * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly - * simplified): - * - * ``` - * var ngBindHtmlDirective = ['$sce', function($sce) { - * return function(scope, element, attr) { - * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { - * element.html(value || ''); - * }); - * }; - * }]; - * ``` - * - * ## Impact on loading templates - * - * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as - * `templateUrl`'s specified by {@link guide/directive directives}. - * - * By default, Angular only loads templates from the same domain and protocol as the application - * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or - * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist - * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. - * - * *Please note*: - * The browser's - * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) - * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) - * policy apply in addition to this and may further restrict whether the template is successfully - * loaded. This means that without the right CORS policy, loading templates from a different domain - * won't work on all browsers. Also, loading templates from `file://` URL does not work on some - * browsers. - * - * ## This feels like too much overhead - * - * It's important to remember that SCE only applies to interpolation expressions. - * - * If your expressions are constant literals, they're automatically trusted and you don't need to - * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. - * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. - * - * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them - * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. - * - * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load - * templates in `ng-include` from your application's domain without having to even know about SCE. - * It blocks loading templates from other domains or loading templates over http from an https - * served document. You can change these by setting your own custom {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link - * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. - * - * This significantly reduces the overhead. It is far easier to pay the small overhead and have an - * application that's secure and can be audited to verify that with much more ease than bolting - * security onto an application later. - * - * <a name="contexts"></a> - * ## What trusted context types are supported? - * - * | Context | Notes | - * |---------------------|----------------| - * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | - * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | - * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. | - * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | - * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | - * - * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> - * - * Each element in these arrays must be one of the following: - * - * - **'self'** - * - The special **string**, `'self'`, can be used to match against all URLs of the **same - * domain** as the application document using the **same protocol**. - * - **String** (except the special value `'self'`) - * - The string is matched against the full *normalized / absolute URL* of the resource - * being tested (substring matches are not good enough.) - * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters - * match themselves. - * - `*`: matches zero or more occurrences of any character other than one of the following 6 - * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use - * in a whitelist. - * - `**`: matches zero or more occurrences of *any* character. As such, it's not - * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g. - * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might - * not have been the intention.) Its usage at the very end of the path is ok. (e.g. - * http://foo.example.com/templates/**). - * - **RegExp** (*see caveat below*) - * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax - * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to - * accidentally introduce a bug when one updates a complex expression (imho, all regexes should - * have good test coverage). For instance, the use of `.` in the regex is correct only in a - * small number of cases. A `.` character in the regex used when matching the scheme or a - * subdomain could be matched against a `:` or literal `.` that was likely not intended. It - * is highly recommended to use the string patterns and only fall back to regular expressions - * as a last resort. - * - The regular expression must be an instance of RegExp (i.e. not a string.) It is - * matched against the **entire** *normalized / absolute URL* of the resource being tested - * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags - * present on the RegExp (such as multiline, global, ignoreCase) are ignored. - * - If you are generating your JavaScript from some other templating engine (not - * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), - * remember to escape your regular expression (and be aware that you might need more than - * one level of escaping depending on your templating engine and the way you interpolated - * the value.) Do make use of your platform's escaping mechanism as it might be good - * enough before coding your own. E.g. Ruby has - * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) - * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). - * Javascript lacks a similar built in function for escaping. Take a look at Google - * Closure library's [goog.string.regExpEscape(s)]( - * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). - * - * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. - * - * ## Show me an example using SCE. - * - * <example module="mySceApp" deps="angular-sanitize.js"> - * <file name="index.html"> - * <div ng-controller="AppController as myCtrl"> - * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> - * <b>User comments</b><br> - * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when - * $sanitize is available. If $sanitize isn't available, this results in an error instead of an - * exploit. - * <div class="well"> - * <div ng-repeat="userComment in myCtrl.userComments"> - * <b>{{userComment.name}}</b>: - * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> - * <br> - * </div> - * </div> - * </div> - * </file> - * - * <file name="script.js"> - * angular.module('mySceApp', ['ngSanitize']) - * .controller('AppController', ['$http', '$templateCache', '$sce', - * function($http, $templateCache, $sce) { - * var self = this; - * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { - * self.userComments = userComments; - * }); - * self.explicitlyTrustedHtml = $sce.trustAsHtml( - * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + - * 'sanitization."">Hover over this text.</span>'); - * }]); - * </file> - * - * <file name="test_data.json"> - * [ - * { "name": "Alice", - * "htmlComment": - * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" - * }, - * { "name": "Bob", - * "htmlComment": "<i>Yes!</i> Am I the only other one?" - * } - * ] - * </file> - * - * <file name="protractor.js" type="protractor"> - * describe('SCE doc demo', function() { - * it('should sanitize untrusted values', function() { - * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) - * .toBe('<span>Is <i>anyone</i> reading this?</span>'); - * }); - * - * it('should NOT sanitize explicitly trusted values', function() { - * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( - * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + - * 'sanitization."">Hover over this text.</span>'); - * }); - * }); - * </file> - * </example> - * - * - * - * ## Can I disable SCE completely? - * - * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits - * for little coding overhead. It will be much harder to take an SCE disabled application and - * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE - * for cases where you have a lot of existing code that was written before SCE was introduced and - * you're migrating them a module at a time. - * - * That said, here's how you can completely disable SCE: - * - * ``` - * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { - * // Completely disable SCE. For demonstration purposes only! - * // Do not use in new projects. - * $sceProvider.enabled(false); - * }); - * ``` - * - */ -/* jshint maxlen: 100 */ - -function $SceProvider() { - var enabled = true; - - /** - * @ngdoc method - * @name $sceProvider#enabled - * @kind function - * - * @param {boolean=} value If provided, then enables/disables SCE. - * @return {boolean} true if SCE is enabled, false otherwise. - * - * @description - * Enables/disables SCE and returns the current value. - */ - this.enabled = function(value) { - if (arguments.length) { - enabled = !!value; - } - return enabled; - }; - - - /* Design notes on the default implementation for SCE. - * - * The API contract for the SCE delegate - * ------------------------------------- - * The SCE delegate object must provide the following 3 methods: - * - * - trustAs(contextEnum, value) - * This method is used to tell the SCE service that the provided value is OK to use in the - * contexts specified by contextEnum. It must return an object that will be accepted by - * getTrusted() for a compatible contextEnum and return this value. - * - * - valueOf(value) - * For values that were not produced by trustAs(), return them as is. For values that were - * produced by trustAs(), return the corresponding input value to trustAs. Basically, if - * trustAs is wrapping the given values into some type, this operation unwraps it when given - * such a value. - * - * - getTrusted(contextEnum, value) - * This function should return the a value that is safe to use in the context specified by - * contextEnum or throw and exception otherwise. - * - * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be - * opaque or wrapped in some holder object. That happens to be an implementation detail. For - * instance, an implementation could maintain a registry of all trusted objects by context. In - * such a case, trustAs() would return the same object that was passed in. getTrusted() would - * return the same object passed in if it was found in the registry under a compatible context or - * throw an exception otherwise. An implementation might only wrap values some of the time based - * on some criteria. getTrusted() might return a value and not throw an exception for special - * constants or objects even if not wrapped. All such implementations fulfill this contract. - * - * - * A note on the inheritance model for SCE contexts - * ------------------------------------------------ - * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This - * is purely an implementation details. - * - * The contract is simply this: - * - * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) - * will also succeed. - * - * Inheritance happens to capture this in a natural way. In some future, we - * may not use inheritance anymore. That is OK because no code outside of - * sce.js and sceSpecs.js would need to be aware of this detail. - */ - - this.$get = ['$parse', '$sceDelegate', function( - $parse, $sceDelegate) { - // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow - // the "expression(javascript expression)" syntax which is insecure. - if (enabled && msie < 8) { - throw $sceMinErr('iequirks', - 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + - 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + - 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); - } - - var sce = shallowCopy(SCE_CONTEXTS); - - /** - * @ngdoc method - * @name $sce#isEnabled - * @kind function - * - * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you - * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. - * - * @description - * Returns a boolean indicating if SCE is enabled. - */ - sce.isEnabled = function() { - return enabled; - }; - sce.trustAs = $sceDelegate.trustAs; - sce.getTrusted = $sceDelegate.getTrusted; - sce.valueOf = $sceDelegate.valueOf; - - if (!enabled) { - sce.trustAs = sce.getTrusted = function(type, value) { return value; }; - sce.valueOf = identity; - } - - /** - * @ngdoc method - * @name $sce#parseAs - * - * @description - * Converts Angular {@link guide/expression expression} into a function. This is like {@link - * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it - * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, - * *result*)} - * - * @param {string} type The kind of SCE context in which this result will be used. - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - sce.parseAs = function sceParseAs(type, expr) { - var parsed = $parse(expr); - if (parsed.literal && parsed.constant) { - return parsed; - } else { - return $parse(expr, function(value) { - return sce.getTrusted(type, value); - }); - } - }; - - /** - * @ngdoc method - * @name $sce#trustAs - * - * @description - * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, - * returns an object that is trusted by angular for use in specified strict contextual - * escaping contexts (such as ng-bind-html, ng-include, any src attribute - * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) - * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual - * escaping. - * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. - */ - - /** - * @ngdoc method - * @name $sce#trustAsHtml - * - * @description - * Shorthand method. `$sce.trustAsHtml(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml - * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsUrl - * - * @description - * Shorthand method. `$sce.trustAsUrl(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl - * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsResourceUrl - * - * @description - * Shorthand method. `$sce.trustAsResourceUrl(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the return - * value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#trustAsJs - * - * @description - * Shorthand method. `$sce.trustAsJs(value)` → - * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} - * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs - * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) - */ - - /** - * @ngdoc method - * @name $sce#getTrusted - * - * @description - * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, - * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the - * originally supplied value if the queried context type is a supertype of the created type. - * If this condition isn't satisfied, throws an exception. - * - * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} - * call. - * @returns {*} The value the was originally provided to - * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. - * Otherwise, throws an exception. - */ - - /** - * @ngdoc method - * @name $sce#getTrustedHtml - * - * @description - * Shorthand method. `$sce.getTrustedHtml(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedCss - * - * @description - * Shorthand method. `$sce.getTrustedCss(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedUrl - * - * @description - * Shorthand method. `$sce.getTrustedUrl(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedResourceUrl - * - * @description - * Shorthand method. `$sce.getTrustedResourceUrl(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} - * - * @param {*} value The value to pass to `$sceDelegate.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` - */ - - /** - * @ngdoc method - * @name $sce#getTrustedJs - * - * @description - * Shorthand method. `$sce.getTrustedJs(value)` → - * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} - * - * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` - */ - - /** - * @ngdoc method - * @name $sce#parseAsHtml - * - * @description - * Shorthand method. `$sce.parseAsHtml(expression string)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsCss - * - * @description - * Shorthand method. `$sce.parseAsCss(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsUrl - * - * @description - * Shorthand method. `$sce.parseAsUrl(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsResourceUrl - * - * @description - * Shorthand method. `$sce.parseAsResourceUrl(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - /** - * @ngdoc method - * @name $sce#parseAsJs - * - * @description - * Shorthand method. `$sce.parseAsJs(value)` → - * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. - */ - - // Shorthand delegations. - var parse = sce.parseAs, - getTrusted = sce.getTrusted, - trustAs = sce.trustAs; - - forEach(SCE_CONTEXTS, function(enumValue, name) { - var lName = lowercase(name); - sce[camelCase("parse_as_" + lName)] = function(expr) { - return parse(enumValue, expr); - }; - sce[camelCase("get_trusted_" + lName)] = function(value) { - return getTrusted(enumValue, value); - }; - sce[camelCase("trust_as_" + lName)] = function(value) { - return trustAs(enumValue, value); - }; - }); - - return sce; - }]; -} - -/** - * !!! This is an undocumented "private" service !!! - * - * @name $sniffer - * @requires $window - * @requires $document - * - * @property {boolean} history Does the browser support html5 history api ? - * @property {boolean} transitions Does the browser support CSS transition events ? - * @property {boolean} animations Does the browser support CSS animation events ? - * - * @description - * This is very simple implementation of testing browser's features. - */ -function $SnifferProvider() { - this.$get = ['$window', '$document', function($window, $document) { - var eventSupport = {}, - android = - toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), - boxee = /Boxee/i.test(($window.navigator || {}).userAgent), - document = $document[0] || {}, - vendorPrefix, - vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/, - bodyStyle = document.body && document.body.style, - transitions = false, - animations = false, - match; - - if (bodyStyle) { - for (var prop in bodyStyle) { - if (match = vendorRegex.exec(prop)) { - vendorPrefix = match[0]; - vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); - break; - } - } - - if (!vendorPrefix) { - vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; - } - - transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); - animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); - - if (android && (!transitions || !animations)) { - transitions = isString(bodyStyle.webkitTransition); - animations = isString(bodyStyle.webkitAnimation); - } - } - - - return { - // Android has history.pushState, but it does not update location correctly - // so let's not use the history API at all. - // http://code.google.com/p/android/issues/detail?id=17471 - // https://github.com/angular/angular.js/issues/904 - - // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has - // so let's not use the history API also - // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined - // jshint -W018 - history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), - // jshint +W018 - hasEvent: function(event) { - // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have - // it. In particular the event is not fired when backspace or delete key are pressed or - // when cut operation is performed. - // IE10+ implements 'input' event but it erroneously fires under various situations, - // e.g. when placeholder changes, or a form is focused. - if (event === 'input' && msie <= 11) return false; - - if (isUndefined(eventSupport[event])) { - var divElm = document.createElement('div'); - eventSupport[event] = 'on' + event in divElm; - } - - return eventSupport[event]; - }, - csp: csp(), - vendorPrefix: vendorPrefix, - transitions: transitions, - animations: animations, - android: android - }; - }]; -} - -var $compileMinErr = minErr('$compile'); - -/** - * @ngdoc service - * @name $templateRequest - * - * @description - * The `$templateRequest` service runs security checks then downloads the provided template using - * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request - * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the - * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the - * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted - * when `tpl` is of type string and `$templateCache` has the matching entry. - * - * @param {string|TrustedResourceUrl} tpl The HTTP request template URL - * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty - * - * @return {Promise} a promise for the HTTP response data of the given URL. - * - * @property {number} totalPendingRequests total amount of pending template requests being downloaded. - */ -function $TemplateRequestProvider() { - this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) { - function handleRequestFn(tpl, ignoreRequestError) { - handleRequestFn.totalPendingRequests++; - - // We consider the template cache holds only trusted templates, so - // there's no need to go through whitelisting again for keys that already - // are included in there. This also makes Angular accept any script - // directive, no matter its name. However, we still need to unwrap trusted - // types. - if (!isString(tpl) || !$templateCache.get(tpl)) { - tpl = $sce.getTrustedResourceUrl(tpl); - } - - var transformResponse = $http.defaults && $http.defaults.transformResponse; - - if (isArray(transformResponse)) { - transformResponse = transformResponse.filter(function(transformer) { - return transformer !== defaultHttpResponseTransform; - }); - } else if (transformResponse === defaultHttpResponseTransform) { - transformResponse = null; - } - - var httpOptions = { - cache: $templateCache, - transformResponse: transformResponse - }; - - return $http.get(tpl, httpOptions) - ['finally'](function() { - handleRequestFn.totalPendingRequests--; - }) - .then(function(response) { - $templateCache.put(tpl, response.data); - return response.data; - }, handleError); - - function handleError(resp) { - if (!ignoreRequestError) { - throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})', - tpl, resp.status, resp.statusText); - } - return $q.reject(resp); - } - } - - handleRequestFn.totalPendingRequests = 0; - - return handleRequestFn; - }]; -} - -function $$TestabilityProvider() { - this.$get = ['$rootScope', '$browser', '$location', - function($rootScope, $browser, $location) { - - /** - * @name $testability - * - * @description - * The private $$testability service provides a collection of methods for use when debugging - * or by automated test and debugging tools. - */ - var testability = {}; - - /** - * @name $$testability#findBindings - * - * @description - * Returns an array of elements that are bound (via ng-bind or {{}}) - * to expressions matching the input. - * - * @param {Element} element The element root to search from. - * @param {string} expression The binding expression to match. - * @param {boolean} opt_exactMatch If true, only returns exact matches - * for the expression. Filters and whitespace are ignored. - */ - testability.findBindings = function(element, expression, opt_exactMatch) { - var bindings = element.getElementsByClassName('ng-binding'); - var matches = []; - forEach(bindings, function(binding) { - var dataBinding = angular.element(binding).data('$binding'); - if (dataBinding) { - forEach(dataBinding, function(bindingName) { - if (opt_exactMatch) { - var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); - if (matcher.test(bindingName)) { - matches.push(binding); - } - } else { - if (bindingName.indexOf(expression) != -1) { - matches.push(binding); - } - } - }); - } - }); - return matches; - }; - - /** - * @name $$testability#findModels - * - * @description - * Returns an array of elements that are two-way found via ng-model to - * expressions matching the input. - * - * @param {Element} element The element root to search from. - * @param {string} expression The model expression to match. - * @param {boolean} opt_exactMatch If true, only returns exact matches - * for the expression. - */ - testability.findModels = function(element, expression, opt_exactMatch) { - var prefixes = ['ng-', 'data-ng-', 'ng\\:']; - for (var p = 0; p < prefixes.length; ++p) { - var attributeEquals = opt_exactMatch ? '=' : '*='; - var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; - var elements = element.querySelectorAll(selector); - if (elements.length) { - return elements; - } - } - }; - - /** - * @name $$testability#getLocation - * - * @description - * Shortcut for getting the location in a browser agnostic way. Returns - * the path, search, and hash. (e.g. /path?a=b#hash) - */ - testability.getLocation = function() { - return $location.url(); - }; - - /** - * @name $$testability#setLocation - * - * @description - * Shortcut for navigating to a location without doing a full page reload. - * - * @param {string} url The location url (path, search and hash, - * e.g. /path?a=b#hash) to go to. - */ - testability.setLocation = function(url) { - if (url !== $location.url()) { - $location.url(url); - $rootScope.$digest(); - } - }; - - /** - * @name $$testability#whenStable - * - * @description - * Calls the callback when $timeout and $http requests are completed. - * - * @param {function} callback - */ - testability.whenStable = function(callback) { - $browser.notifyWhenNoOutstandingRequests(callback); - }; - - return testability; - }]; -} - -function $TimeoutProvider() { - this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', - function($rootScope, $browser, $q, $$q, $exceptionHandler) { - - var deferreds = {}; - - - /** - * @ngdoc service - * @name $timeout - * - * @description - * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch - * block and delegates any exceptions to - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * The return value of calling `$timeout` is a promise, which will be resolved when - * the delay has passed and the timeout function, if provided, is executed. - * - * To cancel a timeout request, call `$timeout.cancel(promise)`. - * - * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to - * synchronously flush the queue of deferred functions. - * - * If you only want a promise that will be resolved after some specified delay - * then you can call `$timeout` without the `fn` function. - * - * @param {function()=} fn A function, whose execution should be delayed. - * @param {number=} [delay=0] Delay in milliseconds. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @param {...*=} Pass additional parameters to the executed function. - * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this - * promise will be resolved with is the return value of the `fn` function. - * - */ - function timeout(fn, delay, invokeApply) { - if (!isFunction(fn)) { - invokeApply = delay; - delay = fn; - fn = noop; - } - - var args = sliceArgs(arguments, 3), - skipApply = (isDefined(invokeApply) && !invokeApply), - deferred = (skipApply ? $$q : $q).defer(), - promise = deferred.promise, - timeoutId; - - timeoutId = $browser.defer(function() { - try { - deferred.resolve(fn.apply(null, args)); - } catch (e) { - deferred.reject(e); - $exceptionHandler(e); - } - finally { - delete deferreds[promise.$$timeoutId]; - } - - if (!skipApply) $rootScope.$apply(); - }, delay); - - promise.$$timeoutId = timeoutId; - deferreds[timeoutId] = deferred; - - return promise; - } - - - /** - * @ngdoc method - * @name $timeout#cancel - * - * @description - * Cancels a task associated with the `promise`. As a result of this, the promise will be - * resolved with a rejection. - * - * @param {Promise=} promise Promise returned by the `$timeout` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - timeout.cancel = function(promise) { - if (promise && promise.$$timeoutId in deferreds) { - deferreds[promise.$$timeoutId].reject('canceled'); - delete deferreds[promise.$$timeoutId]; - return $browser.defer.cancel(promise.$$timeoutId); - } - return false; - }; - - return timeout; - }]; -} - -// NOTE: The usage of window and document instead of $window and $document here is -// deliberate. This service depends on the specific behavior of anchor nodes created by the -// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and -// cause us to break tests. In addition, when the browser resolves a URL for XHR, it -// doesn't know about mocked locations and resolves URLs to the real document - which is -// exactly the behavior needed here. There is little value is mocking these out for this -// service. -var urlParsingNode = document.createElement("a"); -var originUrl = urlResolve(window.location.href); - - -/** - * - * Implementation Notes for non-IE browsers - * ---------------------------------------- - * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, - * results both in the normalizing and parsing of the URL. Normalizing means that a relative - * URL will be resolved into an absolute URL in the context of the application document. - * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related - * properties are all populated to reflect the normalized URL. This approach has wide - * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See - * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html - * - * Implementation Notes for IE - * --------------------------- - * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other - * browsers. However, the parsed components will not be set if the URL assigned did not specify - * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We - * work around that by performing the parsing in a 2nd step by taking a previously normalized - * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the - * properties such as protocol, hostname, port, etc. - * - * References: - * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement - * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html - * http://url.spec.whatwg.org/#urlutils - * https://github.com/angular/angular.js/pull/2902 - * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ - * - * @kind function - * @param {string} url The URL to be parsed. - * @description Normalizes and parses a URL. - * @returns {object} Returns the normalized URL as a dictionary. - * - * | member name | Description | - * |---------------|----------------| - * | href | A normalized version of the provided URL if it was not an absolute URL | - * | protocol | The protocol including the trailing colon | - * | host | The host and port (if the port is non-default) of the normalizedUrl | - * | search | The search params, minus the question mark | - * | hash | The hash string, minus the hash symbol - * | hostname | The hostname - * | port | The port, without ":" - * | pathname | The pathname, beginning with "/" - * - */ -function urlResolve(url) { - var href = url; - - if (msie) { - // Normalize before parse. Refer Implementation Notes on why this is - // done in two steps on IE. - urlParsingNode.setAttribute("href", href); - href = urlParsingNode.href; - } - - urlParsingNode.setAttribute('href', href); - - // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils - return { - href: urlParsingNode.href, - protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', - host: urlParsingNode.host, - search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', - hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', - hostname: urlParsingNode.hostname, - port: urlParsingNode.port, - pathname: (urlParsingNode.pathname.charAt(0) === '/') - ? urlParsingNode.pathname - : '/' + urlParsingNode.pathname - }; -} - -/** - * Parse a request URL and determine whether this is a same-origin request as the application document. - * - * @param {string|object} requestUrl The url of the request as a string that will be resolved - * or a parsed URL object. - * @returns {boolean} Whether the request is for the same origin as the application document. - */ -function urlIsSameOrigin(requestUrl) { - var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; - return (parsed.protocol === originUrl.protocol && - parsed.host === originUrl.host); -} - -/** - * @ngdoc service - * @name $window - * - * @description - * A reference to the browser's `window` object. While `window` - * is globally available in JavaScript, it causes testability problems, because - * it is a global variable. In angular we always refer to it through the - * `$window` service, so it may be overridden, removed or mocked for testing. - * - * Expressions, like the one defined for the `ngClick` directive in the example - * below, are evaluated with respect to the current scope. Therefore, there is - * no risk of inadvertently coding in a dependency on a global value in such an - * expression. - * - * @example - <example module="windowExample"> - <file name="index.html"> - <script> - angular.module('windowExample', []) - .controller('ExampleController', ['$scope', '$window', function($scope, $window) { - $scope.greeting = 'Hello, World!'; - $scope.doGreeting = function(greeting) { - $window.alert(greeting); - }; - }]); - </script> - <div ng-controller="ExampleController"> - <input type="text" ng-model="greeting" aria-label="greeting" /> - <button ng-click="doGreeting(greeting)">ALERT</button> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should display the greeting in the input box', function() { - element(by.model('greeting')).sendKeys('Hello, E2E Tests'); - // If we click the button it will block the test runner - // element(':button').click(); - }); - </file> - </example> - */ -function $WindowProvider() { - this.$get = valueFn(window); -} - -/** - * @name $$cookieReader - * @requires $document - * - * @description - * This is a private service for reading cookies used by $http and ngCookies - * - * @return {Object} a key/value map of the current cookies - */ -function $$CookieReader($document) { - var rawDocument = $document[0] || {}; - var lastCookies = {}; - var lastCookieString = ''; - - function safeDecodeURIComponent(str) { - try { - return decodeURIComponent(str); - } catch (e) { - return str; - } - } - - return function() { - var cookieArray, cookie, i, index, name; - var currentCookieString = rawDocument.cookie || ''; - - if (currentCookieString !== lastCookieString) { - lastCookieString = currentCookieString; - cookieArray = lastCookieString.split('; '); - lastCookies = {}; - - for (i = 0; i < cookieArray.length; i++) { - cookie = cookieArray[i]; - index = cookie.indexOf('='); - if (index > 0) { //ignore nameless cookies - name = safeDecodeURIComponent(cookie.substring(0, index)); - // the first value that is seen for a cookie is the most - // specific one. values for the same cookie name that - // follow are for less specific paths. - if (isUndefined(lastCookies[name])) { - lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); - } - } - } - } - return lastCookies; - }; -} - -$$CookieReader.$inject = ['$document']; - -function $$CookieReaderProvider() { - this.$get = $$CookieReader; -} - -/* global currencyFilter: true, - dateFilter: true, - filterFilter: true, - jsonFilter: true, - limitToFilter: true, - lowercaseFilter: true, - numberFilter: true, - orderByFilter: true, - uppercaseFilter: true, - */ - -/** - * @ngdoc provider - * @name $filterProvider - * @description - * - * Filters are just functions which transform input to an output. However filters need to be - * Dependency Injected. To achieve this a filter definition consists of a factory function which is - * annotated with dependencies and is responsible for creating a filter function. - * - * <div class="alert alert-warning"> - * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - * </div> - * - * ```js - * // Filter registration - * function MyModule($provide, $filterProvider) { - * // create a service to demonstrate injection (not always needed) - * $provide.value('greet', function(name){ - * return 'Hello ' + name + '!'; - * }); - * - * // register a filter factory which uses the - * // greet service to demonstrate DI. - * $filterProvider.register('greet', function(greet){ - * // return the filter function which uses the greet service - * // to generate salutation - * return function(text) { - * // filters need to be forgiving so check input validity - * return text && greet(text) || text; - * }; - * }); - * } - * ``` - * - * The filter function is registered with the `$injector` under the filter name suffix with - * `Filter`. - * - * ```js - * it('should be the same instance', inject( - * function($filterProvider) { - * $filterProvider.register('reverse', function(){ - * return ...; - * }); - * }, - * function($filter, reverseFilter) { - * expect($filter('reverse')).toBe(reverseFilter); - * }); - * ``` - * - * - * For more information about how angular filters work, and how to create your own filters, see - * {@link guide/filter Filters} in the Angular Developer Guide. - */ - -/** - * @ngdoc service - * @name $filter - * @kind function - * @description - * Filters are used for formatting data displayed to the user. - * - * The general syntax in templates is as follows: - * - * {{ expression [| filter_name[:parameter_value] ... ] }} - * - * @param {String} name Name of the filter function to retrieve - * @return {Function} the filter function - * @example - <example name="$filter" module="filterExample"> - <file name="index.html"> - <div ng-controller="MainCtrl"> - <h3>{{ originalText }}</h3> - <h3>{{ filteredText }}</h3> - </div> - </file> - - <file name="script.js"> - angular.module('filterExample', []) - .controller('MainCtrl', function($scope, $filter) { - $scope.originalText = 'hello'; - $scope.filteredText = $filter('uppercase')($scope.originalText); - }); - </file> - </example> - */ -$FilterProvider.$inject = ['$provide']; -function $FilterProvider($provide) { - var suffix = 'Filter'; - - /** - * @ngdoc method - * @name $filterProvider#register - * @param {string|Object} name Name of the filter function, or an object map of filters where - * the keys are the filter names and the values are the filter factories. - * - * <div class="alert alert-warning"> - * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - * </div> - * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered. - * @returns {Object} Registered filter instance, or if a map of filters was provided then a map - * of the registered filter instances. - */ - function register(name, factory) { - if (isObject(name)) { - var filters = {}; - forEach(name, function(filter, key) { - filters[key] = register(key, filter); - }); - return filters; - } else { - return $provide.factory(name + suffix, factory); - } - } - this.register = register; - - this.$get = ['$injector', function($injector) { - return function(name) { - return $injector.get(name + suffix); - }; - }]; - - //////////////////////////////////////// - - /* global - currencyFilter: false, - dateFilter: false, - filterFilter: false, - jsonFilter: false, - limitToFilter: false, - lowercaseFilter: false, - numberFilter: false, - orderByFilter: false, - uppercaseFilter: false, - */ - - register('currency', currencyFilter); - register('date', dateFilter); - register('filter', filterFilter); - register('json', jsonFilter); - register('limitTo', limitToFilter); - register('lowercase', lowercaseFilter); - register('number', numberFilter); - register('orderBy', orderByFilter); - register('uppercase', uppercaseFilter); -} - -/** - * @ngdoc filter - * @name filter - * @kind function - * - * @description - * Selects a subset of items from `array` and returns it as a new array. - * - * @param {Array} array The source array. - * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. - * - * Can be one of: - * - * - `string`: The string is used for matching against the contents of the `array`. All strings or - * objects with string properties in `array` that match this string will be returned. This also - * applies to nested object properties. - * The predicate can be negated by prefixing the string with `!`. - * - * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any - * property of the object or its nested object properties. That's equivalent to the simple - * substring match with a `string` as described above. The predicate can be negated by prefixing - * the string with `!`. - * For example `{name: "!M"}` predicate will return an array of items which have property `name` - * not containing "M". - * - * Note that a named property will match properties on the same level only, while the special - * `$` property will match properties on the same level or deeper. E.g. an array item like - * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but - * **will** be matched by `{$: 'John'}`. - * - * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters. - * The function is called for each element of the array, with the element, its index, and - * the entire array itself as arguments. - * - * The final result is an array of those elements that the predicate returned true for. - * - * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in - * determining if the expected value (from the filter expression) and actual value (from - * the object in the array) should be considered a match. - * - * Can be one of: - * - * - `function(actual, expected)`: - * The function will be given the object value and the predicate value to compare and - * should return true if both values should be considered equal. - * - * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. - * This is essentially strict comparison of expected and actual. - * - * - `false|undefined`: A short hand for a function which will look for a substring match in case - * insensitive way. - * - * Primitive values are converted to strings. Objects are not compared against primitives, - * unless they have a custom `toString` method (e.g. `Date` objects). - * - * @example - <example> - <file name="index.html"> - <div ng-init="friends = [{name:'John', phone:'555-1276'}, - {name:'Mary', phone:'800-BIG-MARY'}, - {name:'Mike', phone:'555-4321'}, - {name:'Adam', phone:'555-5678'}, - {name:'Julie', phone:'555-8765'}, - {name:'Juliette', phone:'555-5678'}]"></div> - - <label>Search: <input ng-model="searchText"></label> - <table id="searchTextResults"> - <tr><th>Name</th><th>Phone</th></tr> - <tr ng-repeat="friend in friends | filter:searchText"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - </tr> - </table> - <hr> - <label>Any: <input ng-model="search.$"></label> <br> - <label>Name only <input ng-model="search.name"></label><br> - <label>Phone only <input ng-model="search.phone"></label><br> - <label>Equality <input type="checkbox" ng-model="strict"></label><br> - <table id="searchObjResults"> - <tr><th>Name</th><th>Phone</th></tr> - <tr ng-repeat="friendObj in friends | filter:search:strict"> - <td>{{friendObj.name}}</td> - <td>{{friendObj.phone}}</td> - </tr> - </table> - </file> - <file name="protractor.js" type="protractor"> - var expectFriendNames = function(expectedNames, key) { - element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { - arr.forEach(function(wd, i) { - expect(wd.getText()).toMatch(expectedNames[i]); - }); - }); - }; - - it('should search across all fields when filtering with a string', function() { - var searchText = element(by.model('searchText')); - searchText.clear(); - searchText.sendKeys('m'); - expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); - - searchText.clear(); - searchText.sendKeys('76'); - expectFriendNames(['John', 'Julie'], 'friend'); - }); - - it('should search in specific fields when filtering with a predicate object', function() { - var searchAny = element(by.model('search.$')); - searchAny.clear(); - searchAny.sendKeys('i'); - expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); - }); - it('should use a equal comparison when comparator is true', function() { - var searchName = element(by.model('search.name')); - var strict = element(by.model('strict')); - searchName.clear(); - searchName.sendKeys('Julie'); - strict.click(); - expectFriendNames(['Julie'], 'friendObj'); - }); - </file> - </example> - */ -function filterFilter() { - return function(array, expression, comparator) { - if (!isArrayLike(array)) { - if (array == null) { - return array; - } else { - throw minErr('filter')('notarray', 'Expected array but received: {0}', array); - } - } - - var expressionType = getTypeForFilter(expression); - var predicateFn; - var matchAgainstAnyProp; - - switch (expressionType) { - case 'function': - predicateFn = expression; - break; - case 'boolean': - case 'null': - case 'number': - case 'string': - matchAgainstAnyProp = true; - //jshint -W086 - case 'object': - //jshint +W086 - predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); - break; - default: - return array; - } - - return Array.prototype.filter.call(array, predicateFn); - }; -} - -// Helper functions for `filterFilter` -function createPredicateFn(expression, comparator, matchAgainstAnyProp) { - var shouldMatchPrimitives = isObject(expression) && ('$' in expression); - var predicateFn; - - if (comparator === true) { - comparator = equals; - } else if (!isFunction(comparator)) { - comparator = function(actual, expected) { - if (isUndefined(actual)) { - // No substring matching against `undefined` - return false; - } - if ((actual === null) || (expected === null)) { - // No substring matching against `null`; only match against `null` - return actual === expected; - } - if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) { - // Should not compare primitives against objects, unless they have custom `toString` method - return false; - } - - actual = lowercase('' + actual); - expected = lowercase('' + expected); - return actual.indexOf(expected) !== -1; - }; - } - - predicateFn = function(item) { - if (shouldMatchPrimitives && !isObject(item)) { - return deepCompare(item, expression.$, comparator, false); - } - return deepCompare(item, expression, comparator, matchAgainstAnyProp); - }; - - return predicateFn; -} - -function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { - var actualType = getTypeForFilter(actual); - var expectedType = getTypeForFilter(expected); - - if ((expectedType === 'string') && (expected.charAt(0) === '!')) { - return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); - } else if (isArray(actual)) { - // In case `actual` is an array, consider it a match - // if ANY of it's items matches `expected` - return actual.some(function(item) { - return deepCompare(item, expected, comparator, matchAgainstAnyProp); - }); - } - - switch (actualType) { - case 'object': - var key; - if (matchAgainstAnyProp) { - for (key in actual) { - if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { - return true; - } - } - return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); - } else if (expectedType === 'object') { - for (key in expected) { - var expectedVal = expected[key]; - if (isFunction(expectedVal) || isUndefined(expectedVal)) { - continue; - } - - var matchAnyProperty = key === '$'; - var actualVal = matchAnyProperty ? actual : actual[key]; - if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { - return false; - } - } - return true; - } else { - return comparator(actual, expected); - } - break; - case 'function': - return false; - default: - return comparator(actual, expected); - } -} - -// Used for easily differentiating between `null` and actual `object` -function getTypeForFilter(val) { - return (val === null) ? 'null' : typeof val; -} - -/** - * @ngdoc filter - * @name currency - * @kind function - * - * @description - * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default - * symbol for current locale is used. - * - * @param {number} amount Input to filter. - * @param {string=} symbol Currency symbol or identifier to be displayed. - * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale - * @returns {string} Formatted number. - * - * - * @example - <example module="currencyExample"> - <file name="index.html"> - <script> - angular.module('currencyExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.amount = 1234.56; - }]); - </script> - <div ng-controller="ExampleController"> - <input type="number" ng-model="amount" aria-label="amount"> <br> - default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> - custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span> - no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should init with 1234.56', function() { - expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); - expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); - expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); - }); - it('should update', function() { - if (browser.params.browser == 'safari') { - // Safari does not understand the minus key. See - // https://github.com/angular/protractor/issues/481 - return; - } - element(by.model('amount')).clear(); - element(by.model('amount')).sendKeys('-1234'); - expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00'); - expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00'); - expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234'); - }); - </file> - </example> - */ -currencyFilter.$inject = ['$locale']; -function currencyFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(amount, currencySymbol, fractionSize) { - if (isUndefined(currencySymbol)) { - currencySymbol = formats.CURRENCY_SYM; - } - - if (isUndefined(fractionSize)) { - fractionSize = formats.PATTERNS[1].maxFrac; - } - - // if null or undefined pass it through - return (amount == null) - ? amount - : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). - replace(/\u00A4/g, currencySymbol); - }; -} - -/** - * @ngdoc filter - * @name number - * @kind function - * - * @description - * Formats a number as text. - * - * If the input is null or undefined, it will just be returned. - * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned. - * If the input is not a number an empty string is returned. - * - * - * @param {number|string} number Number to format. - * @param {(number|string)=} fractionSize Number of decimal places to round the number to. - * If this is not provided then the fraction size is computed from the current locale's number - * formatting pattern. In the case of the default locale, it will be 3. - * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. - * - * @example - <example module="numberFilterExample"> - <file name="index.html"> - <script> - angular.module('numberFilterExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.val = 1234.56789; - }]); - </script> - <div ng-controller="ExampleController"> - <label>Enter number: <input ng-model='val'></label><br> - Default formatting: <span id='number-default'>{{val | number}}</span><br> - No fractions: <span>{{val | number:0}}</span><br> - Negative number: <span>{{-val | number:4}}</span> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should format numbers', function() { - expect(element(by.id('number-default')).getText()).toBe('1,234.568'); - expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); - expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); - }); - - it('should update', function() { - element(by.model('val')).clear(); - element(by.model('val')).sendKeys('3374.333'); - expect(element(by.id('number-default')).getText()).toBe('3,374.333'); - expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); - expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); - }); - </file> - </example> - */ - - -numberFilter.$inject = ['$locale']; -function numberFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(number, fractionSize) { - - // if null or undefined pass it through - return (number == null) - ? number - : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, - fractionSize); - }; -} - -var DECIMAL_SEP = '.'; -function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { - if (isObject(number)) return ''; - - var isNegative = number < 0; - number = Math.abs(number); - - var isInfinity = number === Infinity; - if (!isInfinity && !isFinite(number)) return ''; - - var numStr = number + '', - formatedText = '', - hasExponent = false, - parts = []; - - if (isInfinity) formatedText = '\u221e'; - - if (!isInfinity && numStr.indexOf('e') !== -1) { - var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); - if (match && match[2] == '-' && match[3] > fractionSize + 1) { - number = 0; - } else { - formatedText = numStr; - hasExponent = true; - } - } - - if (!isInfinity && !hasExponent) { - var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; - - // determine fractionSize if it is not specified - if (isUndefined(fractionSize)) { - fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); - } - - // safely round numbers in JS without hitting imprecisions of floating-point arithmetics - // inspired by: - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round - number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); - - var fraction = ('' + number).split(DECIMAL_SEP); - var whole = fraction[0]; - fraction = fraction[1] || ''; - - var i, pos = 0, - lgroup = pattern.lgSize, - group = pattern.gSize; - - if (whole.length >= (lgroup + group)) { - pos = whole.length - lgroup; - for (i = 0; i < pos; i++) { - if ((pos - i) % group === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - } - - for (i = pos; i < whole.length; i++) { - if ((whole.length - i) % lgroup === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - - // format fraction part. - while (fraction.length < fractionSize) { - fraction += '0'; - } - - if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); - } else { - if (fractionSize > 0 && number < 1) { - formatedText = number.toFixed(fractionSize); - number = parseFloat(formatedText); - formatedText = formatedText.replace(DECIMAL_SEP, decimalSep); - } - } - - if (number === 0) { - isNegative = false; - } - - parts.push(isNegative ? pattern.negPre : pattern.posPre, - formatedText, - isNegative ? pattern.negSuf : pattern.posSuf); - return parts.join(''); -} - -function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while (num.length < digits) num = '0' + num; - if (trim) { - num = num.substr(num.length - digits); - } - return neg + num; -} - - -function dateGetter(name, size, offset, trim) { - offset = offset || 0; - return function(date) { - var value = date['get' + name](); - if (offset > 0 || value > -offset) { - value += offset; - } - if (value === 0 && offset == -12) value = 12; - return padNumber(value, size, trim); - }; -} - -function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date['get' + name](); - var get = uppercase(shortForm ? ('SHORT' + name) : name); - - return formats[get][value]; - }; -} - -function timeZoneGetter(date, formats, offset) { - var zone = -1 * offset; - var paddedZone = (zone >= 0) ? "+" : ""; - - paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + - padNumber(Math.abs(zone % 60), 2); - - return paddedZone; -} - -function getFirstThursdayOfYear(year) { - // 0 = index of January - var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); - // 4 = index of Thursday (+1 to account for 1st = 5) - // 11 = index of *next* Thursday (+1 account for 1st = 12) - return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); -} - -function getThursdayThisWeek(datetime) { - return new Date(datetime.getFullYear(), datetime.getMonth(), - // 4 = index of Thursday - datetime.getDate() + (4 - datetime.getDay())); -} - -function weekGetter(size) { - return function(date) { - var firstThurs = getFirstThursdayOfYear(date.getFullYear()), - thisThurs = getThursdayThisWeek(date); - - var diff = +thisThurs - +firstThurs, - result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week - - return padNumber(result, size); - }; -} - -function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; -} - -function eraGetter(date, formats) { - return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; -} - -function longEraGetter(date, formats) { - return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; -} - -var DATE_FORMATS = { - yyyy: dateGetter('FullYear', 4), - yy: dateGetter('FullYear', 2, 0, true), - y: dateGetter('FullYear', 1), - MMMM: dateStrGetter('Month'), - MMM: dateStrGetter('Month', true), - MM: dateGetter('Month', 2, 1), - M: dateGetter('Month', 1, 1), - dd: dateGetter('Date', 2), - d: dateGetter('Date', 1), - HH: dateGetter('Hours', 2), - H: dateGetter('Hours', 1), - hh: dateGetter('Hours', 2, -12), - h: dateGetter('Hours', 1, -12), - mm: dateGetter('Minutes', 2), - m: dateGetter('Minutes', 1), - ss: dateGetter('Seconds', 2), - s: dateGetter('Seconds', 1), - // while ISO 8601 requires fractions to be prefixed with `.` or `,` - // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions - sss: dateGetter('Milliseconds', 3), - EEEE: dateStrGetter('Day'), - EEE: dateStrGetter('Day', true), - a: ampmGetter, - Z: timeZoneGetter, - ww: weekGetter(2), - w: weekGetter(1), - G: eraGetter, - GG: eraGetter, - GGG: eraGetter, - GGGG: longEraGetter -}; - -var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, - NUMBER_STRING = /^\-?\d+$/; - -/** - * @ngdoc filter - * @name date - * @kind function - * - * @description - * Formats `date` to a string based on the requested `format`. - * - * `format` string can be composed of the following elements: - * - * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - * * `'MMMM'`: Month in year (January-December) - * * `'MMM'`: Month in year (Jan-Dec) - * * `'MM'`: Month in year, padded (01-12) - * * `'M'`: Month in year (1-12) - * * `'dd'`: Day in month, padded (01-31) - * * `'d'`: Day in month (1-31) - * * `'EEEE'`: Day in Week,(Sunday-Saturday) - * * `'EEE'`: Day in Week, (Sun-Sat) - * * `'HH'`: Hour in day, padded (00-23) - * * `'H'`: Hour in day (0-23) - * * `'hh'`: Hour in AM/PM, padded (01-12) - * * `'h'`: Hour in AM/PM, (1-12) - * * `'mm'`: Minute in hour, padded (00-59) - * * `'m'`: Minute in hour (0-59) - * * `'ss'`: Second in minute, padded (00-59) - * * `'s'`: Second in minute (0-59) - * * `'sss'`: Millisecond in second, padded (000-999) - * * `'a'`: AM/PM marker - * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) - * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year - * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year - * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') - * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') - * - * `format` string can also be one of the following predefined - * {@link guide/i18n localizable formats}: - * - * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale - * (e.g. Sep 3, 2010 12:05:08 PM) - * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) - * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale - * (e.g. Friday, September 3, 2010) - * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) - * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) - * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) - * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) - * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) - * - * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. - * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence - * (e.g. `"h 'o''clock'"`). - * - * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or - * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its - * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is - * specified in the string input, the time is considered to be in the local timezone. - * @param {string=} format Formatting rules (see Description). If not specified, - * `mediumDate` is used. - * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the - * continental US time zone abbreviations, but for general use, use a time zone offset, for - * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) - * If not specified, the timezone of the browser will be used. - * @returns {string} Formatted string or the input if input is not recognized as date/millis. - * - * @example - <example> - <file name="index.html"> - <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>: - <span>{{1288323623006 | date:'medium'}}</span><br> - <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: - <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br> - <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: - <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br> - <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>: - <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br> - </file> - <file name="protractor.js" type="protractor"> - it('should format date', function() { - expect(element(by.binding("1288323623006 | date:'medium'")).getText()). - toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); - expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). - toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); - expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). - toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); - expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). - toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); - }); - </file> - </example> - */ -dateFilter.$inject = ['$locale']; -function dateFilter($locale) { - - - var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; - // 1 2 3 4 5 6 7 8 9 10 11 - function jsonStringToDate(string) { - var match; - if (match = string.match(R_ISO8601_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0, - dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, - timeSetter = match[8] ? date.setUTCHours : date.setHours; - - if (match[9]) { - tzHour = toInt(match[9] + match[10]); - tzMin = toInt(match[9] + match[11]); - } - dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); - var h = toInt(match[4] || 0) - tzHour; - var m = toInt(match[5] || 0) - tzMin; - var s = toInt(match[6] || 0); - var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); - timeSetter.call(date, h, m, s, ms); - return date; - } - return string; - } - - - return function(date, format, timezone) { - var text = '', - parts = [], - fn, match; - - format = format || 'mediumDate'; - format = $locale.DATETIME_FORMATS[format] || format; - if (isString(date)) { - date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date); - } - - if (isNumber(date)) { - date = new Date(date); - } - - if (!isDate(date) || !isFinite(date.getTime())) { - return date; - } - - while (format) { - match = DATE_FORMATS_SPLIT.exec(format); - if (match) { - parts = concat(parts, match, 1); - format = parts.pop(); - } else { - parts.push(format); - format = null; - } - } - - var dateTimezoneOffset = date.getTimezoneOffset(); - if (timezone) { - dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); - date = convertTimezoneToLocal(date, timezone, true); - } - forEach(parts, function(value) { - fn = DATE_FORMATS[value]; - text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset) - : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); - }); - - return text; - }; -} - - -/** - * @ngdoc filter - * @name json - * @kind function - * - * @description - * Allows you to convert a JavaScript object into JSON string. - * - * This filter is mostly useful for debugging. When using the double curly {{value}} notation - * the binding is automatically converted to JSON. - * - * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. - * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. - * @returns {string} JSON string. - * - * - * @example - <example> - <file name="index.html"> - <pre id="default-spacing">{{ {'name':'value'} | json }}</pre> - <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre> - </file> - <file name="protractor.js" type="protractor"> - it('should jsonify filtered objects', function() { - expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); - expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); - }); - </file> - </example> - * - */ -function jsonFilter() { - return function(object, spacing) { - if (isUndefined(spacing)) { - spacing = 2; - } - return toJson(object, spacing); - }; -} - - -/** - * @ngdoc filter - * @name lowercase - * @kind function - * @description - * Converts string to lowercase. - * @see angular.lowercase - */ -var lowercaseFilter = valueFn(lowercase); - - -/** - * @ngdoc filter - * @name uppercase - * @kind function - * @description - * Converts string to uppercase. - * @see angular.uppercase - */ -var uppercaseFilter = valueFn(uppercase); - -/** - * @ngdoc filter - * @name limitTo - * @kind function - * - * @description - * Creates a new array or string containing only a specified number of elements. The elements - * are taken from either the beginning or the end of the source array, string or number, as specified by - * the value and sign (positive or negative) of `limit`. If a number is used as input, it is - * converted to a string. - * - * @param {Array|string|number} input Source array, string or number to be limited. - * @param {string|number} limit The length of the returned array or string. If the `limit` number - * is positive, `limit` number of items from the beginning of the source array/string are copied. - * If the number is negative, `limit` number of items from the end of the source array/string - * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined, - * the input will be returned unchanged. - * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin` - * indicates an offset from the end of `input`. Defaults to `0`. - * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array - * had less than `limit` elements. - * - * @example - <example module="limitToExample"> - <file name="index.html"> - <script> - angular.module('limitToExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.numbers = [1,2,3,4,5,6,7,8,9]; - $scope.letters = "abcdefghi"; - $scope.longNumber = 2345432342; - $scope.numLimit = 3; - $scope.letterLimit = 3; - $scope.longNumberLimit = 3; - }]); - </script> - <div ng-controller="ExampleController"> - <label> - Limit {{numbers}} to: - <input type="number" step="1" ng-model="numLimit"> - </label> - <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> - <label> - Limit {{letters}} to: - <input type="number" step="1" ng-model="letterLimit"> - </label> - <p>Output letters: {{ letters | limitTo:letterLimit }}</p> - <label> - Limit {{longNumber}} to: - <input type="number" step="1" ng-model="longNumberLimit"> - </label> - <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p> - </div> - </file> - <file name="protractor.js" type="protractor"> - var numLimitInput = element(by.model('numLimit')); - var letterLimitInput = element(by.model('letterLimit')); - var longNumberLimitInput = element(by.model('longNumberLimit')); - var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); - var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); - var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); - - it('should limit the number array to first three items', function() { - expect(numLimitInput.getAttribute('value')).toBe('3'); - expect(letterLimitInput.getAttribute('value')).toBe('3'); - expect(longNumberLimitInput.getAttribute('value')).toBe('3'); - expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); - expect(limitedLetters.getText()).toEqual('Output letters: abc'); - expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); - }); - - // There is a bug in safari and protractor that doesn't like the minus key - // it('should update the output when -3 is entered', function() { - // numLimitInput.clear(); - // numLimitInput.sendKeys('-3'); - // letterLimitInput.clear(); - // letterLimitInput.sendKeys('-3'); - // longNumberLimitInput.clear(); - // longNumberLimitInput.sendKeys('-3'); - // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); - // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); - // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); - // }); - - it('should not exceed the maximum size of input array', function() { - numLimitInput.clear(); - numLimitInput.sendKeys('100'); - letterLimitInput.clear(); - letterLimitInput.sendKeys('100'); - longNumberLimitInput.clear(); - longNumberLimitInput.sendKeys('100'); - expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); - expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); - expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); - }); - </file> - </example> -*/ -function limitToFilter() { - return function(input, limit, begin) { - if (Math.abs(Number(limit)) === Infinity) { - limit = Number(limit); - } else { - limit = toInt(limit); - } - if (isNaN(limit)) return input; - - if (isNumber(input)) input = input.toString(); - if (!isArray(input) && !isString(input)) return input; - - begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); - begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin; - - if (limit >= 0) { - return input.slice(begin, begin + limit); - } else { - if (begin === 0) { - return input.slice(limit, input.length); - } else { - return input.slice(Math.max(0, begin + limit), begin); - } - } - }; -} - -/** - * @ngdoc filter - * @name orderBy - * @kind function - * - * @description - * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically - * for strings and numerically for numbers. Note: if you notice numbers are not being sorted - * as expected, make sure they are actually being saved as numbers and not strings. - * - * @param {Array} array The array to sort. - * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be - * used by the comparator to determine the order of elements. - * - * Can be one of: - * - * - `function`: Getter function. The result of this function will be sorted using the - * `<`, `===`, `>` operator. - * - `string`: An Angular expression. The result of this expression is used to compare elements - * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by - * 3 first characters of a property called `name`). The result of a constant expression - * is interpreted as a property name to be used in comparisons (for example `"special name"` - * to sort object by the value of their `special name` property). An expression can be - * optionally prefixed with `+` or `-` to control ascending or descending sort order - * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array - * element itself is used to compare where sorting. - * - `Array`: An array of function or string predicates. The first predicate in the array - * is used for sorting, but when two items are equivalent, the next predicate is used. - * - * If the predicate is missing or empty then it defaults to `'+'`. - * - * @param {boolean=} reverse Reverse the order of the array. - * @returns {Array} Sorted copy of the source array. - * - * - * @example - * The example below demonstrates a simple ngRepeat, where the data is sorted - * by age in descending order (predicate is set to `'-age'`). - * `reverse` is not set, which means it defaults to `false`. - <example module="orderByExample"> - <file name="index.html"> - <script> - angular.module('orderByExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.friends = - [{name:'John', phone:'555-1212', age:10}, - {name:'Mary', phone:'555-9876', age:19}, - {name:'Mike', phone:'555-4321', age:21}, - {name:'Adam', phone:'555-5678', age:35}, - {name:'Julie', phone:'555-8765', age:29}]; - }]); - </script> - <div ng-controller="ExampleController"> - <table class="friend"> - <tr> - <th>Name</th> - <th>Phone Number</th> - <th>Age</th> - </tr> - <tr ng-repeat="friend in friends | orderBy:'-age'"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <td>{{friend.age}}</td> - </tr> - </table> - </div> - </file> - </example> - * - * The predicate and reverse parameters can be controlled dynamically through scope properties, - * as shown in the next example. - * @example - <example module="orderByExample"> - <file name="index.html"> - <script> - angular.module('orderByExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.friends = - [{name:'John', phone:'555-1212', age:10}, - {name:'Mary', phone:'555-9876', age:19}, - {name:'Mike', phone:'555-4321', age:21}, - {name:'Adam', phone:'555-5678', age:35}, - {name:'Julie', phone:'555-8765', age:29}]; - $scope.predicate = 'age'; - $scope.reverse = true; - $scope.order = function(predicate) { - $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false; - $scope.predicate = predicate; - }; - }]); - </script> - <style type="text/css"> - .sortorder:after { - content: '\25b2'; - } - .sortorder.reverse:after { - content: '\25bc'; - } - </style> - <div ng-controller="ExampleController"> - <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre> - <hr/> - [ <a href="" ng-click="predicate=''">unsorted</a> ] - <table class="friend"> - <tr> - <th> - <a href="" ng-click="order('name')">Name</a> - <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span> - </th> - <th> - <a href="" ng-click="order('phone')">Phone Number</a> - <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span> - </th> - <th> - <a href="" ng-click="order('age')">Age</a> - <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span> - </th> - </tr> - <tr ng-repeat="friend in friends | orderBy:predicate:reverse"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <td>{{friend.age}}</td> - </tr> - </table> - </div> - </file> - </example> - * - * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the - * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the - * desired parameters. - * - * Example: - * - * @example - <example module="orderByExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <table class="friend"> - <tr> - <th><a href="" ng-click="reverse=false;order('name', false)">Name</a> - (<a href="" ng-click="order('-name',false)">^</a>)</th> - <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th> - <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th> - </tr> - <tr ng-repeat="friend in friends"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <td>{{friend.age}}</td> - </tr> - </table> - </div> - </file> - - <file name="script.js"> - angular.module('orderByExample', []) - .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { - var orderBy = $filter('orderBy'); - $scope.friends = [ - { name: 'John', phone: '555-1212', age: 10 }, - { name: 'Mary', phone: '555-9876', age: 19 }, - { name: 'Mike', phone: '555-4321', age: 21 }, - { name: 'Adam', phone: '555-5678', age: 35 }, - { name: 'Julie', phone: '555-8765', age: 29 } - ]; - $scope.order = function(predicate, reverse) { - $scope.friends = orderBy($scope.friends, predicate, reverse); - }; - $scope.order('-age',false); - }]); - </file> -</example> - */ -orderByFilter.$inject = ['$parse']; -function orderByFilter($parse) { - return function(array, sortPredicate, reverseOrder) { - - if (!(isArrayLike(array))) return array; - - if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; } - if (sortPredicate.length === 0) { sortPredicate = ['+']; } - - var predicates = processPredicates(sortPredicate, reverseOrder); - // Add a predicate at the end that evaluates to the element index. This makes the - // sort stable as it works as a tie-breaker when all the input predicates cannot - // distinguish between two elements. - predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1}); - - // The next three lines are a version of a Swartzian Transform idiom from Perl - // (sometimes called the Decorate-Sort-Undecorate idiom) - // See https://en.wikipedia.org/wiki/Schwartzian_transform - var compareValues = Array.prototype.map.call(array, getComparisonObject); - compareValues.sort(doComparison); - array = compareValues.map(function(item) { return item.value; }); - - return array; - - function getComparisonObject(value, index) { - return { - value: value, - predicateValues: predicates.map(function(predicate) { - return getPredicateValue(predicate.get(value), index); - }) - }; - } - - function doComparison(v1, v2) { - var result = 0; - for (var index=0, length = predicates.length; index < length; ++index) { - result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending; - if (result) break; - } - return result; - } - }; - - function processPredicates(sortPredicate, reverseOrder) { - reverseOrder = reverseOrder ? -1 : 1; - return sortPredicate.map(function(predicate) { - var descending = 1, get = identity; - - if (isFunction(predicate)) { - get = predicate; - } else if (isString(predicate)) { - if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { - descending = predicate.charAt(0) == '-' ? -1 : 1; - predicate = predicate.substring(1); - } - if (predicate !== '') { - get = $parse(predicate); - if (get.constant) { - var key = get(); - get = function(value) { return value[key]; }; - } - } - } - return { get: get, descending: descending * reverseOrder }; - }); - } - - function isPrimitive(value) { - switch (typeof value) { - case 'number': /* falls through */ - case 'boolean': /* falls through */ - case 'string': - return true; - default: - return false; - } - } - - function objectValue(value, index) { - // If `valueOf` is a valid function use that - if (typeof value.valueOf === 'function') { - value = value.valueOf(); - if (isPrimitive(value)) return value; - } - // If `toString` is a valid function and not the one from `Object.prototype` use that - if (hasCustomToString(value)) { - value = value.toString(); - if (isPrimitive(value)) return value; - } - // We have a basic object so we use the position of the object in the collection - return index; - } - - function getPredicateValue(value, index) { - var type = typeof value; - if (value === null) { - type = 'string'; - value = 'null'; - } else if (type === 'string') { - value = value.toLowerCase(); - } else if (type === 'object') { - value = objectValue(value, index); - } - return { value: value, type: type }; - } - - function compare(v1, v2) { - var result = 0; - if (v1.type === v2.type) { - if (v1.value !== v2.value) { - result = v1.value < v2.value ? -1 : 1; - } - } else { - result = v1.type < v2.type ? -1 : 1; - } - return result; - } -} - -function ngDirective(directive) { - if (isFunction(directive)) { - directive = { - link: directive - }; - } - directive.restrict = directive.restrict || 'AC'; - return valueFn(directive); -} - -/** - * @ngdoc directive - * @name a - * @restrict E - * - * @description - * Modifies the default behavior of the html A tag so that the default action is prevented when - * the href attribute is empty. - * - * This change permits the easy creation of action links with the `ngClick` directive - * without changing the location or causing page reloads, e.g.: - * `<a href="" ng-click="list.addItem()">Add Item</a>` - */ -var htmlAnchorDirective = valueFn({ - restrict: 'E', - compile: function(element, attr) { - if (!attr.href && !attr.xlinkHref) { - return function(scope, element) { - // If the linked element is not an anchor tag anymore, do nothing - if (element[0].nodeName.toLowerCase() !== 'a') return; - - // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. - var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? - 'xlink:href' : 'href'; - element.on('click', function(event) { - // if we have no href url, then don't navigate anywhere. - if (!element.attr(href)) { - event.preventDefault(); - } - }); - }; - } - } -}); - -/** - * @ngdoc directive - * @name ngHref - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in an href attribute will - * make the link go to the wrong URL if the user clicks it before - * Angular has a chance to replace the `{{hash}}` markup with its - * value. Until Angular replaces the markup the link will be broken - * and will most likely return a 404 error. The `ngHref` directive - * solves this problem. - * - * The wrong way to write it: - * ```html - * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a> - * ``` - * - * The correct way to write it: - * ```html - * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a> - * ``` - * - * @element A - * @param {template} ngHref any string which can contain `{{}}` markup. - * - * @example - * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes - * in links and their different behaviors: - <example> - <file name="index.html"> - <input ng-model="value" /><br /> - <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> - <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> - <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> - <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> - <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> - <a id="link-6" ng-href="{{value}}">link</a> (link, change location) - </file> - <file name="protractor.js" type="protractor"> - it('should execute ng-click but not reload when href without value', function() { - element(by.id('link-1')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('1'); - expect(element(by.id('link-1')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click but not reload when href empty string', function() { - element(by.id('link-2')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('2'); - expect(element(by.id('link-2')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click and change url when ng-href specified', function() { - expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); - - element(by.id('link-3')).click(); - - // At this point, we navigate away from an Angular page, so we need - // to use browser.driver to get the base webdriver. - - browser.wait(function() { - return browser.driver.getCurrentUrl().then(function(url) { - return url.match(/\/123$/); - }); - }, 5000, 'page should navigate to /123'); - }); - - it('should execute ng-click but not reload when href empty string and name specified', function() { - element(by.id('link-4')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('4'); - expect(element(by.id('link-4')).getAttribute('href')).toBe(''); - }); - - it('should execute ng-click but not reload when no href but name specified', function() { - element(by.id('link-5')).click(); - expect(element(by.model('value')).getAttribute('value')).toEqual('5'); - expect(element(by.id('link-5')).getAttribute('href')).toBe(null); - }); - - it('should only change url when only ng-href', function() { - element(by.model('value')).clear(); - element(by.model('value')).sendKeys('6'); - expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); - - element(by.id('link-6')).click(); - - // At this point, we navigate away from an Angular page, so we need - // to use browser.driver to get the base webdriver. - browser.wait(function() { - return browser.driver.getCurrentUrl().then(function(url) { - return url.match(/\/6$/); - }); - }, 5000, 'page should navigate to /6'); - }); - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngSrc - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in a `src` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrc` directive solves this problem. - * - * The buggy way to write it: - * ```html - * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/> - * ``` - * - * The correct way to write it: - * ```html - * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" /> - * ``` - * - * @element IMG - * @param {template} ngSrc any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ngSrcset - * @restrict A - * @priority 99 - * - * @description - * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrcset` directive solves this problem. - * - * The buggy way to write it: - * ```html - * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/> - * ``` - * - * The correct way to write it: - * ```html - * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" /> - * ``` - * - * @element IMG - * @param {template} ngSrcset any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ngDisabled - * @restrict A - * @priority 100 - * - * @description - * - * This directive sets the `disabled` attribute on the element if the - * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. - * - * A special directive is necessary because we cannot use interpolation inside the `disabled` - * attribute. The following example would make the button enabled on Chrome/Firefox - * but not on older IEs: - * - * ```html - * <!-- See below for an example of ng-disabled being used correctly --> - * <div ng-init="isDisabled = false"> - * <button disabled="{{isDisabled}}">Disabled</button> - * </div> - * ``` - * - * This is because the HTML specification does not require browsers to preserve the values of - * boolean attributes such as `disabled` (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * - * @example - <example> - <file name="index.html"> - <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/> - <button ng-model="button" ng-disabled="checked">Button</button> - </file> - <file name="protractor.js" type="protractor"> - it('should toggle button', function() { - expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); - element(by.model('checked')).click(); - expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); - }); - </file> - </example> - * - * @element INPUT - * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, - * then the `disabled` attribute will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngChecked - * @restrict A - * @priority 100 - * - * @description - * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy. - * - * Note that this directive should not be used together with {@link ngModel `ngModel`}, - * as this can lead to unexpected behavior. - * - * ### Why do we need `ngChecked`? - * - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as checked. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngChecked` directive solves this problem for the `checked` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - <example> - <file name="index.html"> - <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/> - <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input"> - </file> - <file name="protractor.js" type="protractor"> - it('should check both checkBoxes', function() { - expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); - element(by.model('master')).click(); - expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); - }); - </file> - </example> - * - * @element INPUT - * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, - * then the `checked` attribute will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngReadonly - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as readonly. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngReadonly` directive solves this problem for the `readonly` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - <example> - <file name="index.html"> - <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/> - <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" /> - </file> - <file name="protractor.js" type="protractor"> - it('should toggle readonly attr', function() { - expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); - element(by.model('checked')).click(); - expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); - }); - </file> - </example> - * - * @element INPUT - * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, - * then special attribute "readonly" will be set on the element - */ - - -/** - * @ngdoc directive - * @name ngSelected - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as selected. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngSelected` directive solves this problem for the `selected` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * - * @example - <example> - <file name="index.html"> - <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/> - <select aria-label="ngSelected demo"> - <option>Hello!</option> - <option id="greet" ng-selected="selected">Greetings!</option> - </select> - </file> - <file name="protractor.js" type="protractor"> - it('should select Greetings!', function() { - expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); - element(by.model('selected')).click(); - expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); - }); - </file> - </example> - * - * @element OPTION - * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, - * then special attribute "selected" will be set on the element - */ - -/** - * @ngdoc directive - * @name ngOpen - * @restrict A - * @priority 100 - * - * @description - * The HTML specification does not require browsers to preserve the values of boolean attributes - * such as open. (Their presence means true and their absence means false.) - * If we put an Angular interpolation expression into such an attribute then the - * binding information would be lost when the browser removes the attribute. - * The `ngOpen` directive solves this problem for the `open` attribute. - * This complementary directive is not removed by the browser and so provides - * a permanent reliable place to store the binding information. - * @example - <example> - <file name="index.html"> - <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/> - <details id="details" ng-open="open"> - <summary>Show/Hide me</summary> - </details> - </file> - <file name="protractor.js" type="protractor"> - it('should toggle open', function() { - expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); - element(by.model('open')).click(); - expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); - }); - </file> - </example> - * - * @element DETAILS - * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, - * then special attribute "open" will be set on the element - */ - -var ngAttributeAliasDirectives = {}; - -// boolean attrs are evaluated -forEach(BOOLEAN_ATTR, function(propName, attrName) { - // binding to multiple is not supported - if (propName == "multiple") return; - - function defaultLinkFn(scope, element, attr) { - scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { - attr.$set(attrName, !!value); - }); - } - - var normalized = directiveNormalize('ng-' + attrName); - var linkFn = defaultLinkFn; - - if (propName === 'checked') { - linkFn = function(scope, element, attr) { - // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input - if (attr.ngModel !== attr[normalized]) { - defaultLinkFn(scope, element, attr); - } - }; - } - - ngAttributeAliasDirectives[normalized] = function() { - return { - restrict: 'A', - priority: 100, - link: linkFn - }; - }; -}); - -// aliased input attrs are evaluated -forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { - ngAttributeAliasDirectives[ngAttr] = function() { - return { - priority: 100, - link: function(scope, element, attr) { - //special case ngPattern when a literal regular expression value - //is used as the expression (this way we don't have to watch anything). - if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") { - var match = attr.ngPattern.match(REGEX_STRING_REGEXP); - if (match) { - attr.$set("ngPattern", new RegExp(match[1], match[2])); - return; - } - } - - scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { - attr.$set(ngAttr, value); - }); - } - }; - }; -}); - -// ng-src, ng-srcset, ng-href are interpolated -forEach(['src', 'srcset', 'href'], function(attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 99, // it needs to run after the attributes are interpolated - link: function(scope, element, attr) { - var propName = attrName, - name = attrName; - - if (attrName === 'href' && - toString.call(element.prop('href')) === '[object SVGAnimatedString]') { - name = 'xlinkHref'; - attr.$attr[name] = 'xlink:href'; - propName = null; - } - - attr.$observe(normalized, function(value) { - if (!value) { - if (attrName === 'href') { - attr.$set(name, null); - } - return; - } - - attr.$set(name, value); - - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect. - // we use attr[attrName] value since $set can sanitize the url. - if (msie && propName) element.prop(propName, attr[name]); - }); - } - }; - }; -}); - -/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true - */ -var nullFormCtrl = { - $addControl: noop, - $$renameControl: nullFormRenameControl, - $removeControl: noop, - $setValidity: noop, - $setDirty: noop, - $setPristine: noop, - $setSubmitted: noop -}, -SUBMITTED_CLASS = 'ng-submitted'; - -function nullFormRenameControl(control, name) { - control.$name = name; -} - -/** - * @ngdoc type - * @name form.FormController - * - * @property {boolean} $pristine True if user has not interacted with the form yet. - * @property {boolean} $dirty True if user has already interacted with the form. - * @property {boolean} $valid True if all of the containing forms and controls are valid. - * @property {boolean} $invalid True if at least one containing control or form is invalid. - * @property {boolean} $pending True if at least one containing control or form is pending. - * @property {boolean} $submitted True if user has submitted the form even if its invalid. - * - * @property {Object} $error Is an object hash, containing references to controls or - * forms with failing validators, where: - * - * - keys are validation tokens (error names), - * - values are arrays of controls or forms that have a failing validator for given error name. - * - * Built-in validation tokens: - * - * - `email` - * - `max` - * - `maxlength` - * - `min` - * - `minlength` - * - `number` - * - `pattern` - * - `required` - * - `url` - * - `date` - * - `datetimelocal` - * - `time` - * - `week` - * - `month` - * - * @description - * `FormController` keeps track of all its controls and nested forms as well as the state of them, - * such as being valid/invalid or dirty/pristine. - * - * Each {@link ng.directive:form form} directive creates an instance - * of `FormController`. - * - */ -//asks for $scope to fool the BC controller module -FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; -function FormController(element, attrs, $scope, $animate, $interpolate) { - var form = this, - controls = []; - - // init state - form.$error = {}; - form.$$success = {}; - form.$pending = undefined; - form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope); - form.$dirty = false; - form.$pristine = true; - form.$valid = true; - form.$invalid = false; - form.$submitted = false; - form.$$parentForm = nullFormCtrl; - - /** - * @ngdoc method - * @name form.FormController#$rollbackViewValue - * - * @description - * Rollback all form controls pending updates to the `$modelValue`. - * - * Updates may be pending by a debounced event or because the input is waiting for a some future - * event defined in `ng-model-options`. This method is typically needed by the reset button of - * a form that uses `ng-model-options` to pend updates. - */ - form.$rollbackViewValue = function() { - forEach(controls, function(control) { - control.$rollbackViewValue(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$commitViewValue - * - * @description - * Commit all form controls pending updates to the `$modelValue`. - * - * Updates may be pending by a debounced event or because the input is waiting for a some future - * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` - * usually handles calling this in response to input events. - */ - form.$commitViewValue = function() { - forEach(controls, function(control) { - control.$commitViewValue(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$addControl - * @param {object} control control object, either a {@link form.FormController} or an - * {@link ngModel.NgModelController} - * - * @description - * Register a control with the form. Input elements using ngModelController do this automatically - * when they are linked. - * - * Note that the current state of the control will not be reflected on the new parent form. This - * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine` - * state. - * - * However, if the method is used programmatically, for example by adding dynamically created controls, - * or controls that have been previously removed without destroying their corresponding DOM element, - * it's the developers responsiblity to make sure the current state propagates to the parent form. - * - * For example, if an input control is added that is already `$dirty` and has `$error` properties, - * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form. - */ - form.$addControl = function(control) { - // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored - // and not added to the scope. Now we throw an error. - assertNotHasOwnProperty(control.$name, 'input'); - controls.push(control); - - if (control.$name) { - form[control.$name] = control; - } - - control.$$parentForm = form; - }; - - // Private API: rename a form control - form.$$renameControl = function(control, newName) { - var oldName = control.$name; - - if (form[oldName] === control) { - delete form[oldName]; - } - form[newName] = control; - control.$name = newName; - }; - - /** - * @ngdoc method - * @name form.FormController#$removeControl - * @param {object} control control object, either a {@link form.FormController} or an - * {@link ngModel.NgModelController} - * - * @description - * Deregister a control from the form. - * - * Input elements using ngModelController do this automatically when they are destroyed. - * - * Note that only the removed control's validation state (`$errors`etc.) will be removed from the - * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be - * different from case to case. For example, removing the only `$dirty` control from a form may or - * may not mean that the form is still `$dirty`. - */ - form.$removeControl = function(control) { - if (control.$name && form[control.$name] === control) { - delete form[control.$name]; - } - forEach(form.$pending, function(value, name) { - form.$setValidity(name, null, control); - }); - forEach(form.$error, function(value, name) { - form.$setValidity(name, null, control); - }); - forEach(form.$$success, function(value, name) { - form.$setValidity(name, null, control); - }); - - arrayRemove(controls, control); - control.$$parentForm = nullFormCtrl; - }; - - - /** - * @ngdoc method - * @name form.FormController#$setValidity - * - * @description - * Sets the validity of a form control. - * - * This method will also propagate to parent forms. - */ - addSetValidityMethod({ - ctrl: this, - $element: element, - set: function(object, property, controller) { - var list = object[property]; - if (!list) { - object[property] = [controller]; - } else { - var index = list.indexOf(controller); - if (index === -1) { - list.push(controller); - } - } - }, - unset: function(object, property, controller) { - var list = object[property]; - if (!list) { - return; - } - arrayRemove(list, controller); - if (list.length === 0) { - delete object[property]; - } - }, - $animate: $animate - }); - - /** - * @ngdoc method - * @name form.FormController#$setDirty - * - * @description - * Sets the form to a dirty state. - * - * This method can be called to add the 'ng-dirty' class and set the form to a dirty - * state (ng-dirty class). This method will also propagate to parent forms. - */ - form.$setDirty = function() { - $animate.removeClass(element, PRISTINE_CLASS); - $animate.addClass(element, DIRTY_CLASS); - form.$dirty = true; - form.$pristine = false; - form.$$parentForm.$setDirty(); - }; - - /** - * @ngdoc method - * @name form.FormController#$setPristine - * - * @description - * Sets the form to its pristine state. - * - * This method can be called to remove the 'ng-dirty' class and set the form to its pristine - * state (ng-pristine class). This method will also propagate to all the controls contained - * in this form. - * - * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after - * saving or resetting it. - */ - form.$setPristine = function() { - $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); - form.$dirty = false; - form.$pristine = true; - form.$submitted = false; - forEach(controls, function(control) { - control.$setPristine(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$setUntouched - * - * @description - * Sets the form to its untouched state. - * - * This method can be called to remove the 'ng-touched' class and set the form controls to their - * untouched state (ng-untouched class). - * - * Setting a form controls back to their untouched state is often useful when setting the form - * back to its pristine state. - */ - form.$setUntouched = function() { - forEach(controls, function(control) { - control.$setUntouched(); - }); - }; - - /** - * @ngdoc method - * @name form.FormController#$setSubmitted - * - * @description - * Sets the form to its submitted state. - */ - form.$setSubmitted = function() { - $animate.addClass(element, SUBMITTED_CLASS); - form.$submitted = true; - form.$$parentForm.$setSubmitted(); - }; -} - -/** - * @ngdoc directive - * @name ngForm - * @restrict EAC - * - * @description - * Nestable alias of {@link ng.directive:form `form`} directive. HTML - * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a - * sub-group of controls needs to be determined. - * - * Note: the purpose of `ngForm` is to group controls, - * but not to be a replacement for the `<form>` tag with all of its capabilities - * (e.g. posting to the server, ...). - * - * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - */ - - /** - * @ngdoc directive - * @name form - * @restrict E - * - * @description - * Directive that instantiates - * {@link form.FormController FormController}. - * - * If the `name` attribute is specified, the form controller is published onto the current scope under - * this name. - * - * # Alias: {@link ng.directive:ngForm `ngForm`} - * - * In Angular, forms can be nested. This means that the outer form is valid when all of the child - * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so - * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to - * `<form>` but can be nested. This allows you to have nested forms, which is very useful when - * using Angular validation directives in forms that are dynamically generated using the - * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` - * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an - * `ngForm` directive and nest these in an outer `form` element. - * - * - * # CSS classes - * - `ng-valid` is set if the form is valid. - * - `ng-invalid` is set if the form is invalid. - * - `ng-pending` is set if the form is pending. - * - `ng-pristine` is set if the form is pristine. - * - `ng-dirty` is set if the form is dirty. - * - `ng-submitted` is set if the form was submitted. - * - * Keep in mind that ngAnimate can detect each of these classes when added and removed. - * - * - * # Submitting a form and preventing the default action - * - * Since the role of forms in client-side Angular applications is different than in classical - * roundtrip apps, it is desirable for the browser not to translate the form submission into a full - * page reload that sends the data to the server. Instead some javascript logic should be triggered - * to handle the form submission in an application-specific way. - * - * For this reason, Angular prevents the default action (form submission to the server) unless the - * `<form>` element has an `action` attribute specified. - * - * You can use one of the following two ways to specify what javascript method should be called when - * a form is submitted: - * - * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element - * - {@link ng.directive:ngClick ngClick} directive on the first - * button or input field of type submit (input[type=submit]) - * - * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} - * or {@link ng.directive:ngClick ngClick} directives. - * This is because of the following form submission rules in the HTML specification: - * - * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ngSubmit`) - * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter - * doesn't trigger submit - * - if a form has one or more input fields and one or more buttons or input[type=submit] then - * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) - * - * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is - * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` - * to have access to the updated model. - * - * ## Animation Hooks - * - * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. - * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any - * other validations that are performed within the form. Animations in ngForm are similar to how - * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well - * as JS animations. - * - * The following example shows a simple way to utilize CSS transitions to style a form element - * that has been rendered as invalid after it has been validated: - * - * <pre> - * //be sure to include ngAnimate as a module to hook into more - * //advanced animations - * .my-form { - * transition:0.5s linear all; - * background: white; - * } - * .my-form.ng-invalid { - * background: red; - * color:white; - * } - * </pre> - * - * @example - <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample"> - <file name="index.html"> - <script> - angular.module('formExample', []) - .controller('FormController', ['$scope', function($scope) { - $scope.userType = 'guest'; - }]); - </script> - <style> - .my-form { - transition:all linear 0.5s; - background: transparent; - } - .my-form.ng-invalid { - background: red; - } - </style> - <form name="myForm" ng-controller="FormController" class="my-form"> - userType: <input name="input" ng-model="userType" required> - <span class="error" ng-show="myForm.input.$error.required">Required!</span><br> - <code>userType = {{userType}}</code><br> - <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br> - <code>myForm.input.$error = {{myForm.input.$error}}</code><br> - <code>myForm.$valid = {{myForm.$valid}}</code><br> - <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br> - </form> - </file> - <file name="protractor.js" type="protractor"> - it('should initialize to model', function() { - var userType = element(by.binding('userType')); - var valid = element(by.binding('myForm.input.$valid')); - - expect(userType.getText()).toContain('guest'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - var userType = element(by.binding('userType')); - var valid = element(by.binding('myForm.input.$valid')); - var userInput = element(by.model('userType')); - - userInput.clear(); - userInput.sendKeys(''); - - expect(userType.getText()).toEqual('userType ='); - expect(valid.getText()).toContain('false'); - }); - </file> - </example> - * - * @param {string=} name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - */ -var formDirectiveFactory = function(isNgForm) { - return ['$timeout', '$parse', function($timeout, $parse) { - var formDirective = { - name: 'form', - restrict: isNgForm ? 'EAC' : 'E', - require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form - controller: FormController, - compile: function ngFormCompile(formElement, attr) { - // Setup initial state of the control - formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); - - var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); - - return { - pre: function ngFormPreLink(scope, formElement, attr, ctrls) { - var controller = ctrls[0]; - - // if `action` attr is not present on the form, prevent the default action (submission) - if (!('action' in attr)) { - // we can't use jq events because if a form is destroyed during submission the default - // action is not prevented. see #1238 - // - // IE 9 is not affected because it doesn't fire a submit event and try to do a full - // page reload if the form was destroyed by submission of the form via a click handler - // on a button in the form. Looks like an IE9 specific bug. - var handleFormSubmission = function(event) { - scope.$apply(function() { - controller.$commitViewValue(); - controller.$setSubmitted(); - }); - - event.preventDefault(); - }; - - addEventListenerFn(formElement[0], 'submit', handleFormSubmission); - - // unregister the preventDefault listener so that we don't not leak memory but in a - // way that will achieve the prevention of the default action. - formElement.on('$destroy', function() { - $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); - }, 0, false); - }); - } - - var parentFormCtrl = ctrls[1] || controller.$$parentForm; - parentFormCtrl.$addControl(controller); - - var setter = nameAttr ? getSetter(controller.$name) : noop; - - if (nameAttr) { - setter(scope, controller); - attr.$observe(nameAttr, function(newValue) { - if (controller.$name === newValue) return; - setter(scope, undefined); - controller.$$parentForm.$$renameControl(controller, newValue); - setter = getSetter(controller.$name); - setter(scope, controller); - }); - } - formElement.on('$destroy', function() { - controller.$$parentForm.$removeControl(controller); - setter(scope, undefined); - extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards - }); - } - }; - } - }; - - return formDirective; - - function getSetter(expression) { - if (expression === '') { - //create an assignable expression, so forms with an empty name can be renamed later - return $parse('this[""]').assign; - } - return $parse(expression).assign || noop; - } - }]; -}; - -var formDirective = formDirectiveFactory(); -var ngFormDirective = formDirectiveFactory(true); - -/* global VALID_CLASS: false, - INVALID_CLASS: false, - PRISTINE_CLASS: false, - DIRTY_CLASS: false, - UNTOUCHED_CLASS: false, - TOUCHED_CLASS: false, - ngModelMinErr: false, -*/ - -// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 -var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; -var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; -var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; -var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/; -var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; -var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/; -var MONTH_REGEXP = /^(\d{4})-(\d\d)$/; -var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; - -var inputType = { - - /** - * @ngdoc input - * @name input[text] - * - * @description - * Standard HTML text input with angular data binding, inherited by most of the `input` elements. - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Adds `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - * This parameter is ignored for input[type=password] controls, which will never trim the - * input. - * - * @example - <example name="text-input-directive" module="textInputExample"> - <file name="index.html"> - <script> - angular.module('textInputExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.example = { - text: 'guest', - word: /^\s*\w*\s*$/ - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label>Single word: - <input type="text" name="input" ng-model="example.text" - ng-pattern="example.word" required ng-trim="false"> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.pattern"> - Single word only!</span> - </div> - <tt>text = {{example.text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var text = element(by.binding('example.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('guest'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if multi word', function() { - input.clear(); - input.sendKeys('hello world'); - - expect(valid.getText()).toContain('false'); - }); - </file> - </example> - */ - 'text': textInputType, - - /** - * @ngdoc input - * @name input[date] - * - * @description - * Input with date validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 - * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many - * modern browsers do not yet support this input type, it is important to provide cues to users on the - * expected input format via a placeholder or label. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a - * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute - * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5 - * constraint validation. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be - * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute - * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5 - * constraint validation. - * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string - * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. - * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string - * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="date-input-directive" module="dateInputExample"> - <file name="index.html"> - <script> - angular.module('dateInputExample', []) - .controller('DateController', ['$scope', function($scope) { - $scope.example = { - value: new Date(2013, 9, 22) - }; - }]); - </script> - <form name="myForm" ng-controller="DateController as dateCtrl"> - <label for="exampleInput">Pick a date in 2013:</label> - <input type="date" id="exampleInput" name="input" ng-model="example.value" - placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required /> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.date"> - Not a valid date!</span> - </div> - <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (see https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-10-22'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01-01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - </file> - </example> - */ - 'date': createDateInputType('date', DATE_REGEXP, - createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), - 'yyyy-MM-dd'), - - /** - * @ngdoc input - * @name input[datetime-local] - * - * @description - * Input with datetime validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation - * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). - * Note that `min` will also add native HTML5 constraint validation. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation - * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). - * Note that `max` will also add native HTML5 constraint validation. - * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string - * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. - * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string - * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="datetimelocal-input-directive" module="dateExample"> - <file name="index.html"> - <script> - angular.module('dateExample', []) - .controller('DateController', ['$scope', function($scope) { - $scope.example = { - value: new Date(2010, 11, 28, 14, 57) - }; - }]); - </script> - <form name="myForm" ng-controller="DateController as dateCtrl"> - <label for="exampleInput">Pick a date between in 2013:</label> - <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value" - placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required /> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.datetimelocal"> - Not a valid date!</span> - </div> - <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2010-12-28T14:57:00'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01-01T23:59:00'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - </file> - </example> - */ - 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, - createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), - 'yyyy-MM-ddTHH:mm:ss.sss'), - - /** - * @ngdoc input - * @name input[time] - * - * @description - * Input with time validation and transformation. In browsers that do not yet support - * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a - * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this - * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add - * native HTML5 constraint validation. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this - * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add - * native HTML5 constraint validation. - * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the - * `ngMin` expression evaluates to. Note that it does not set the `min` attribute. - * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the - * `ngMax` expression evaluates to. Note that it does not set the `max` attribute. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="time-input-directive" module="timeExample"> - <file name="index.html"> - <script> - angular.module('timeExample', []) - .controller('DateController', ['$scope', function($scope) { - $scope.example = { - value: new Date(1970, 0, 1, 14, 57, 0) - }; - }]); - </script> - <form name="myForm" ng-controller="DateController as dateCtrl"> - <label for="exampleInput">Pick a between 8am and 5pm:</label> - <input type="time" id="exampleInput" name="input" ng-model="example.value" - placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required /> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.time"> - Not a valid date!</span> - </div> - <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value | date: "HH:mm:ss"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('14:57:00'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('23:59:00'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - </file> - </example> - */ - 'time': createDateInputType('time', TIME_REGEXP, - createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), - 'HH:mm:ss.sss'), - - /** - * @ngdoc input - * @name input[week] - * - * @description - * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support - * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * week format (yyyy-W##), for example: `2013-W02`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this - * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add - * native HTML5 constraint validation. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this - * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add - * native HTML5 constraint validation. - * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string - * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. - * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string - * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="week-input-directive" module="weekExample"> - <file name="index.html"> - <script> - angular.module('weekExample', []) - .controller('DateController', ['$scope', function($scope) { - $scope.example = { - value: new Date(2013, 0, 3) - }; - }]); - </script> - <form name="myForm" ng-controller="DateController as dateCtrl"> - <label>Pick a date between in 2013: - <input id="exampleInput" type="week" name="input" ng-model="example.value" - placeholder="YYYY-W##" min="2012-W32" - max="2013-W52" required /> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.week"> - Not a valid date!</span> - </div> - <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value | date: "yyyy-Www"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-W01'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-W01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - </file> - </example> - */ - 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), - - /** - * @ngdoc input - * @name input[month] - * - * @description - * Input with month validation and transformation. In browsers that do not yet support - * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 - * month format (yyyy-MM), for example: `2009-01`. - * - * The model must always be a Date object, otherwise Angular will throw an error. - * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. - * If the model is not set to the first of the month, the next view to model update will set it - * to the first of the month. - * - * The timezone to be used to read/write the `Date` instance in the model can be defined using - * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this - * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add - * native HTML5 constraint validation. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this - * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add - * native HTML5 constraint validation. - * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string - * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. - * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string - * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. - - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="month-input-directive" module="monthExample"> - <file name="index.html"> - <script> - angular.module('monthExample', []) - .controller('DateController', ['$scope', function($scope) { - $scope.example = { - value: new Date(2013, 9, 1) - }; - }]); - </script> - <form name="myForm" ng-controller="DateController as dateCtrl"> - <label for="exampleInput">Pick a month in 2013:</label> - <input id="exampleInput" type="month" name="input" ng-model="example.value" - placeholder="yyyy-MM" min="2013-01" max="2013-12" required /> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.month"> - Not a valid month!</span> - </div> - <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value | date: "yyyy-MM"')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - // currently protractor/webdriver does not support - // sending keys to all known HTML5 input controls - // for various browsers (https://github.com/angular/protractor/issues/562). - function setInput(val) { - // set the value of the element and force validation. - var scr = "var ipt = document.getElementById('exampleInput'); " + - "ipt.value = '" + val + "';" + - "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; - browser.executeScript(scr); - } - - it('should initialize to model', function() { - expect(value.getText()).toContain('2013-10'); - expect(valid.getText()).toContain('myForm.input.$valid = true'); - }); - - it('should be invalid if empty', function() { - setInput(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - - it('should be invalid if over max', function() { - setInput('2015-01'); - expect(value.getText()).toContain(''); - expect(valid.getText()).toContain('myForm.input.$valid = false'); - }); - </file> - </example> - */ - 'month': createDateInputType('month', MONTH_REGEXP, - createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), - 'yyyy-MM'), - - /** - * @ngdoc input - * @name input[number] - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - * <div class="alert alert-warning"> - * The model must always be of type `number` otherwise Angular will throw an error. - * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt} - * error docs for more information and an example of how to convert your model if necessary. - * </div> - * - * ## Issues with HTML5 constraint validation - * - * In browsers that follow the - * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29), - * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}. - * If a non-number is entered in the input, the browser will report the value as an empty string, - * which means the view / model values in `ngModel` and subsequently the scope value - * will also be an empty string. - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. - * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="number-input-directive" module="numberExample"> - <file name="index.html"> - <script> - angular.module('numberExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.example = { - value: 12 - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label>Number: - <input type="number" name="input" ng-model="example.value" - min="0" max="99" required> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.number"> - Not valid number!</span> - </div> - <tt>value = {{example.value}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var value = element(by.binding('example.value')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('example.value')); - - it('should initialize to model', function() { - expect(value.getText()).toContain('12'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if over max', function() { - input.clear(); - input.sendKeys('123'); - expect(value.getText()).toEqual('value ='); - expect(valid.getText()).toContain('false'); - }); - </file> - </example> - */ - 'number': numberInputType, - - - /** - * @ngdoc input - * @name input[url] - * - * @description - * Text input with URL validation. Sets the `url` validation error key if the content is not a - * valid URL. - * - * <div class="alert alert-warning"> - * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex - * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify - * the built-in validators (see the {@link guide/forms Forms guide}) - * </div> - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="url-input-directive" module="urlExample"> - <file name="index.html"> - <script> - angular.module('urlExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.url = { - text: 'http://google.com' - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label>URL: - <input type="url" name="input" ng-model="url.text" required> - <label> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.url"> - Not valid url!</span> - </div> - <tt>text = {{url.text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var text = element(by.binding('url.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('url.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('http://google.com'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if not url', function() { - input.clear(); - input.sendKeys('box'); - - expect(valid.getText()).toContain('false'); - }); - </file> - </example> - */ - 'url': urlInputType, - - - /** - * @ngdoc input - * @name input[email] - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - * <div class="alert alert-warning"> - * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex - * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can - * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) - * </div> - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of - * any length. - * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string - * that contains the regular expression body that will be converted to a regular expression - * as in the ngPattern directive. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="email-input-directive" module="emailExample"> - <file name="index.html"> - <script> - angular.module('emailExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.email = { - text: 'me@example.com' - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label>Email: - <input type="email" name="input" ng-model="email.text" required> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.email"> - Not valid email!</span> - </div> - <tt>text = {{email.text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - var text = element(by.binding('email.text')); - var valid = element(by.binding('myForm.input.$valid')); - var input = element(by.model('email.text')); - - it('should initialize to model', function() { - expect(text.getText()).toContain('me@example.com'); - expect(valid.getText()).toContain('true'); - }); - - it('should be invalid if empty', function() { - input.clear(); - input.sendKeys(''); - expect(text.getText()).toEqual('text ='); - expect(valid.getText()).toContain('false'); - }); - - it('should be invalid if not email', function() { - input.clear(); - input.sendKeys('xxx'); - - expect(valid.getText()).toContain('false'); - }); - </file> - </example> - */ - 'email': emailInputType, - - - /** - * @ngdoc input - * @name input[radio] - * - * @description - * HTML radio button. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string} value The value to which the `ngModel` expression should be set when selected. - * Note that `value` only supports `string` values, i.e. the scope model needs to be a string, - * too. Use `ngValue` if you need complex models (`number`, `object`, ...). - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio - * is selected. Should be used instead of the `value` attribute if you need - * a non-string `ngModel` (`boolean`, `array`, ...). - * - * @example - <example name="radio-input-directive" module="radioExample"> - <file name="index.html"> - <script> - angular.module('radioExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.color = { - name: 'blue' - }; - $scope.specialValue = { - "id": "12345", - "value": "green" - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label> - <input type="radio" ng-model="color.name" value="red"> - Red - </label><br/> - <label> - <input type="radio" ng-model="color.name" ng-value="specialValue"> - Green - </label><br/> - <label> - <input type="radio" ng-model="color.name" value="blue"> - Blue - </label><br/> - <tt>color = {{color.name | json}}</tt><br/> - </form> - Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. - </file> - <file name="protractor.js" type="protractor"> - it('should change state', function() { - var color = element(by.binding('color.name')); - - expect(color.getText()).toContain('blue'); - - element.all(by.model('color.name')).get(0).click(); - - expect(color.getText()).toContain('red'); - }); - </file> - </example> - */ - 'radio': radioInputType, - - - /** - * @ngdoc input - * @name input[checkbox] - * - * @description - * HTML checkbox. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {expression=} ngTrueValue The value to which the expression should be set when selected. - * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <example name="checkbox-input-directive" module="checkboxExample"> - <file name="index.html"> - <script> - angular.module('checkboxExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.checkboxModel = { - value1 : true, - value2 : 'YES' - }; - }]); - </script> - <form name="myForm" ng-controller="ExampleController"> - <label>Value1: - <input type="checkbox" ng-model="checkboxModel.value1"> - </label><br/> - <label>Value2: - <input type="checkbox" ng-model="checkboxModel.value2" - ng-true-value="'YES'" ng-false-value="'NO'"> - </label><br/> - <tt>value1 = {{checkboxModel.value1}}</tt><br/> - <tt>value2 = {{checkboxModel.value2}}</tt><br/> - </form> - </file> - <file name="protractor.js" type="protractor"> - it('should change state', function() { - var value1 = element(by.binding('checkboxModel.value1')); - var value2 = element(by.binding('checkboxModel.value2')); - - expect(value1.getText()).toContain('true'); - expect(value2.getText()).toContain('YES'); - - element(by.model('checkboxModel.value1')).click(); - element(by.model('checkboxModel.value2')).click(); - - expect(value1.getText()).toContain('false'); - expect(value2.getText()).toContain('NO'); - }); - </file> - </example> - */ - 'checkbox': checkboxInputType, - - 'hidden': noop, - 'button': noop, - 'submit': noop, - 'reset': noop, - 'file': noop -}; - -function stringBasedInputType(ctrl) { - ctrl.$formatters.push(function(value) { - return ctrl.$isEmpty(value) ? value : value.toString(); - }); -} - -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); -} - -function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { - var type = lowercase(element[0].type); - - // In composition mode, users are still inputing intermediate text buffer, - // hold the listener until composition is done. - // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent - if (!$sniffer.android) { - var composing = false; - - element.on('compositionstart', function(data) { - composing = true; - }); - - element.on('compositionend', function() { - composing = false; - listener(); - }); - } - - var listener = function(ev) { - if (timeout) { - $browser.defer.cancel(timeout); - timeout = null; - } - if (composing) return; - var value = element.val(), - event = ev && ev.type; - - // By default we will trim the value - // If the attribute ng-trim exists we will avoid trimming - // If input type is 'password', the value is never trimmed - if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { - value = trim(value); - } - - // If a control is suffering from bad input (due to native validators), browsers discard its - // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the - // control's value is the same empty value twice in a row. - if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { - ctrl.$setViewValue(value, event); - } - }; - - // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the - // input event on backspace, delete or cut - if ($sniffer.hasEvent('input')) { - element.on('input', listener); - } else { - var timeout; - - var deferListener = function(ev, input, origValue) { - if (!timeout) { - timeout = $browser.defer(function() { - timeout = null; - if (!input || input.value !== origValue) { - listener(ev); - } - }); - } - }; - - element.on('keydown', function(event) { - var key = event.keyCode; - - // ignore - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - deferListener(event, this, this.value); - }); - - // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it - if ($sniffer.hasEvent('paste')) { - element.on('paste cut', deferListener); - } - } - - // if user paste into input using mouse on older browser - // or form autocomplete on newer browser, we need "change" event to catch it - element.on('change', listener); - - ctrl.$render = function() { - // Workaround for Firefox validation #12102. - var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; - if (element.val() !== value) { - element.val(value); - } - }; -} - -function weekParser(isoWeek, existingDate) { - if (isDate(isoWeek)) { - return isoWeek; - } - - if (isString(isoWeek)) { - WEEK_REGEXP.lastIndex = 0; - var parts = WEEK_REGEXP.exec(isoWeek); - if (parts) { - var year = +parts[1], - week = +parts[2], - hours = 0, - minutes = 0, - seconds = 0, - milliseconds = 0, - firstThurs = getFirstThursdayOfYear(year), - addDays = (week - 1) * 7; - - if (existingDate) { - hours = existingDate.getHours(); - minutes = existingDate.getMinutes(); - seconds = existingDate.getSeconds(); - milliseconds = existingDate.getMilliseconds(); - } - - return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); - } - } - - return NaN; -} - -function createDateParser(regexp, mapping) { - return function(iso, date) { - var parts, map; - - if (isDate(iso)) { - return iso; - } - - if (isString(iso)) { - // When a date is JSON'ified to wraps itself inside of an extra - // set of double quotes. This makes the date parsing code unable - // to match the date string and parse it as a date. - if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') { - iso = iso.substring(1, iso.length - 1); - } - if (ISO_DATE_REGEXP.test(iso)) { - return new Date(iso); - } - regexp.lastIndex = 0; - parts = regexp.exec(iso); - - if (parts) { - parts.shift(); - if (date) { - map = { - yyyy: date.getFullYear(), - MM: date.getMonth() + 1, - dd: date.getDate(), - HH: date.getHours(), - mm: date.getMinutes(), - ss: date.getSeconds(), - sss: date.getMilliseconds() / 1000 - }; - } else { - map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; - } - - forEach(parts, function(part, index) { - if (index < mapping.length) { - map[mapping[index]] = +part; - } - }); - return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); - } - } - - return NaN; - }; -} - -function createDateInputType(type, regexp, parseDate, format) { - return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { - badInputChecker(scope, element, attr, ctrl); - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; - var previousDate; - - ctrl.$$parserName = type; - ctrl.$parsers.push(function(value) { - if (ctrl.$isEmpty(value)) return null; - if (regexp.test(value)) { - // Note: We cannot read ctrl.$modelValue, as there might be a different - // parser/formatter in the processing chain so that the model - // contains some different data format! - var parsedDate = parseDate(value, previousDate); - if (timezone) { - parsedDate = convertTimezoneToLocal(parsedDate, timezone); - } - return parsedDate; - } - return undefined; - }); - - ctrl.$formatters.push(function(value) { - if (value && !isDate(value)) { - throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); - } - if (isValidDate(value)) { - previousDate = value; - if (previousDate && timezone) { - previousDate = convertTimezoneToLocal(previousDate, timezone, true); - } - return $filter('date')(value, format, timezone); - } else { - previousDate = null; - return ''; - } - }); - - if (isDefined(attr.min) || attr.ngMin) { - var minVal; - ctrl.$validators.min = function(value) { - return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; - }; - attr.$observe('min', function(val) { - minVal = parseObservedDateValue(val); - ctrl.$validate(); - }); - } - - if (isDefined(attr.max) || attr.ngMax) { - var maxVal; - ctrl.$validators.max = function(value) { - return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; - }; - attr.$observe('max', function(val) { - maxVal = parseObservedDateValue(val); - ctrl.$validate(); - }); - } - - function isValidDate(value) { - // Invalid Date: getTime() returns NaN - return value && !(value.getTime && value.getTime() !== value.getTime()); - } - - function parseObservedDateValue(val) { - return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val; - } - }; -} - -function badInputChecker(scope, element, attr, ctrl) { - var node = element[0]; - var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); - if (nativeValidation) { - ctrl.$parsers.push(function(value) { - var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; - // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430): - // - also sets validity.badInput (should only be validity.typeMismatch). - // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email) - // - can ignore this case as we can still read out the erroneous email... - return validity.badInput && !validity.typeMismatch ? undefined : value; - }); - } -} - -function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { - badInputChecker(scope, element, attr, ctrl); - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - - ctrl.$$parserName = 'number'; - ctrl.$parsers.push(function(value) { - if (ctrl.$isEmpty(value)) return null; - if (NUMBER_REGEXP.test(value)) return parseFloat(value); - return undefined; - }); - - ctrl.$formatters.push(function(value) { - if (!ctrl.$isEmpty(value)) { - if (!isNumber(value)) { - throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); - } - value = value.toString(); - } - return value; - }); - - if (isDefined(attr.min) || attr.ngMin) { - var minVal; - ctrl.$validators.min = function(value) { - return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; - }; - - attr.$observe('min', function(val) { - if (isDefined(val) && !isNumber(val)) { - val = parseFloat(val, 10); - } - minVal = isNumber(val) && !isNaN(val) ? val : undefined; - // TODO(matsko): implement validateLater to reduce number of validations - ctrl.$validate(); - }); - } - - if (isDefined(attr.max) || attr.ngMax) { - var maxVal; - ctrl.$validators.max = function(value) { - return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; - }; - - attr.$observe('max', function(val) { - if (isDefined(val) && !isNumber(val)) { - val = parseFloat(val, 10); - } - maxVal = isNumber(val) && !isNaN(val) ? val : undefined; - // TODO(matsko): implement validateLater to reduce number of validations - ctrl.$validate(); - }); - } -} - -function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { - // Note: no badInputChecker here by purpose as `url` is only a validation - // in browsers, i.e. we can always read out input.value even if it is not valid! - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); - - ctrl.$$parserName = 'url'; - ctrl.$validators.url = function(modelValue, viewValue) { - var value = modelValue || viewValue; - return ctrl.$isEmpty(value) || URL_REGEXP.test(value); - }; -} - -function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { - // Note: no badInputChecker here by purpose as `url` is only a validation - // in browsers, i.e. we can always read out input.value even if it is not valid! - baseInputType(scope, element, attr, ctrl, $sniffer, $browser); - stringBasedInputType(ctrl); - - ctrl.$$parserName = 'email'; - ctrl.$validators.email = function(modelValue, viewValue) { - var value = modelValue || viewValue; - return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); - }; -} - -function radioInputType(scope, element, attr, ctrl) { - // make the name unique, if not defined - if (isUndefined(attr.name)) { - element.attr('name', nextUid()); - } - - var listener = function(ev) { - if (element[0].checked) { - ctrl.$setViewValue(attr.value, ev && ev.type); - } - }; - - element.on('click', listener); - - ctrl.$render = function() { - var value = attr.value; - element[0].checked = (value == ctrl.$viewValue); - }; - - attr.$observe('value', ctrl.$render); -} - -function parseConstantExpr($parse, context, name, expression, fallback) { - var parseFn; - if (isDefined(expression)) { - parseFn = $parse(expression); - if (!parseFn.constant) { - throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + - '`{1}`.', name, expression); - } - return parseFn(context); - } - return fallback; -} - -function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { - var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); - var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); - - var listener = function(ev) { - ctrl.$setViewValue(element[0].checked, ev && ev.type); - }; - - element.on('click', listener); - - ctrl.$render = function() { - element[0].checked = ctrl.$viewValue; - }; - - // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` - // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert - // it to a boolean. - ctrl.$isEmpty = function(value) { - return value === false; - }; - - ctrl.$formatters.push(function(value) { - return equals(value, trueValue); - }); - - ctrl.$parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @name textarea - * @restrict E - * - * @description - * HTML textarea element control with angular data-binding. The data-binding and validation - * properties of this element are exactly the same as those of the - * {@link ng.directive:input input element}. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any - * length. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - */ - - -/** - * @ngdoc directive - * @name input - * @restrict E - * - * @description - * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, - * input state control, and validation. - * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. - * - * <div class="alert alert-warning"> - * **Note:** Not every feature offered is available for all input types. - * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. - * </div> - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {boolean=} ngRequired Sets `required` attribute if set to true - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any - * length. - * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match - * a RegExp found by evaluating the Angular expression given in the attribute value. - * If the expression evaluates to a RegExp object, then this is used directly. - * If the expression evaluates to a string, then it will be converted to a RegExp - * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to - * `new RegExp('^abc$')`.<br /> - * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to - * start at the index of the last search's match, thus not taking the whole input value into - * account. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. - * This parameter is ignored for input[type=password] controls, which will never trim the - * input. - * - * @example - <example name="input-directive" module="inputExample"> - <file name="index.html"> - <script> - angular.module('inputExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.user = {name: 'guest', last: 'visitor'}; - }]); - </script> - <div ng-controller="ExampleController"> - <form name="myForm"> - <label> - User name: - <input type="text" name="userName" ng-model="user.name" required> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.userName.$error.required"> - Required!</span> - </div> - <label> - Last name: - <input type="text" name="lastName" ng-model="user.last" - ng-minlength="3" ng-maxlength="10"> - </label> - <div role="alert"> - <span class="error" ng-show="myForm.lastName.$error.minlength"> - Too short!</span> - <span class="error" ng-show="myForm.lastName.$error.maxlength"> - Too long!</span> - </div> - </form> - <hr> - <tt>user = {{user}}</tt><br/> - <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/> - <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/> - <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/> - <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/> - <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/> - </div> - </file> - <file name="protractor.js" type="protractor"> - var user = element(by.exactBinding('user')); - var userNameValid = element(by.binding('myForm.userName.$valid')); - var lastNameValid = element(by.binding('myForm.lastName.$valid')); - var lastNameError = element(by.binding('myForm.lastName.$error')); - var formValid = element(by.binding('myForm.$valid')); - var userNameInput = element(by.model('user.name')); - var userLastInput = element(by.model('user.last')); - - it('should initialize to model', function() { - expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); - expect(userNameValid.getText()).toContain('true'); - expect(formValid.getText()).toContain('true'); - }); - - it('should be invalid if empty when required', function() { - userNameInput.clear(); - userNameInput.sendKeys(''); - - expect(user.getText()).toContain('{"last":"visitor"}'); - expect(userNameValid.getText()).toContain('false'); - expect(formValid.getText()).toContain('false'); - }); - - it('should be valid if empty when min length is set', function() { - userLastInput.clear(); - userLastInput.sendKeys(''); - - expect(user.getText()).toContain('{"name":"guest","last":""}'); - expect(lastNameValid.getText()).toContain('true'); - expect(formValid.getText()).toContain('true'); - }); - - it('should be invalid if less than required min length', function() { - userLastInput.clear(); - userLastInput.sendKeys('xx'); - - expect(user.getText()).toContain('{"name":"guest"}'); - expect(lastNameValid.getText()).toContain('false'); - expect(lastNameError.getText()).toContain('minlength'); - expect(formValid.getText()).toContain('false'); - }); - - it('should be invalid if longer than max length', function() { - userLastInput.clear(); - userLastInput.sendKeys('some ridiculously long name'); - - expect(user.getText()).toContain('{"name":"guest"}'); - expect(lastNameValid.getText()).toContain('false'); - expect(lastNameError.getText()).toContain('maxlength'); - expect(formValid.getText()).toContain('false'); - }); - </file> - </example> - */ -var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', - function($browser, $sniffer, $filter, $parse) { - return { - restrict: 'E', - require: ['?ngModel'], - link: { - pre: function(scope, element, attr, ctrls) { - if (ctrls[0]) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, - $browser, $filter, $parse); - } - } - } - }; -}]; - - - -var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; -/** - * @ngdoc directive - * @name ngValue - * - * @description - * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`}, - * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to - * the bound value. - * - * `ngValue` is useful when dynamically generating lists of radio buttons using - * {@link ngRepeat `ngRepeat`}, as shown below. - * - * Likewise, `ngValue` can be used to generate `<option>` elements for - * the {@link select `select`} element. In that case however, only strings are supported - * for the `value `attribute, so the resulting `ngModel` will always be a string. - * Support for `select` models with non-string values is available via `ngOptions`. - * - * @element input - * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute - * of the `input` element - * - * @example - <example name="ngValue-directive" module="valueExample"> - <file name="index.html"> - <script> - angular.module('valueExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.names = ['pizza', 'unicorns', 'robots']; - $scope.my = { favorite: 'unicorns' }; - }]); - </script> - <form ng-controller="ExampleController"> - <h2>Which is your favorite?</h2> - <label ng-repeat="name in names" for="{{name}}"> - {{name}} - <input type="radio" - ng-model="my.favorite" - ng-value="name" - id="{{name}}" - name="favorite"> - </label> - <div>You chose {{my.favorite}}</div> - </form> - </file> - <file name="protractor.js" type="protractor"> - var favorite = element(by.binding('my.favorite')); - - it('should initialize to model', function() { - expect(favorite.getText()).toContain('unicorns'); - }); - it('should bind the values to the inputs', function() { - element.all(by.model('my.favorite')).get(0).click(); - expect(favorite.getText()).toContain('pizza'); - }); - </file> - </example> - */ -var ngValueDirective = function() { - return { - restrict: 'A', - priority: 100, - compile: function(tpl, tplAttr) { - if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { - return function ngValueConstantLink(scope, elm, attr) { - attr.$set('value', scope.$eval(attr.ngValue)); - }; - } else { - return function ngValueLink(scope, elm, attr) { - scope.$watch(attr.ngValue, function valueWatchAction(value) { - attr.$set('value', value); - }); - }; - } - } - }; -}; - -/** - * @ngdoc directive - * @name ngBind - * @restrict AC - * - * @description - * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element - * with the value of a given expression, and to update the text content when the value of that - * expression changes. - * - * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like - * `{{ expression }}` which is similar but less verbose. - * - * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily - * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an - * element attribute, it makes the bindings invisible to the user while the page is loading. - * - * An alternative solution to this problem would be using the - * {@link ng.directive:ngCloak ngCloak} directive. - * - * - * @element ANY - * @param {expression} ngBind {@link guide/expression Expression} to evaluate. - * - * @example - * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. - <example module="bindExample"> - <file name="index.html"> - <script> - angular.module('bindExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.name = 'Whirled'; - }]); - </script> - <div ng-controller="ExampleController"> - <label>Enter name: <input type="text" ng-model="name"></label><br> - Hello <span ng-bind="name"></span>! - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-bind', function() { - var nameInput = element(by.model('name')); - - expect(element(by.binding('name')).getText()).toBe('Whirled'); - nameInput.clear(); - nameInput.sendKeys('world'); - expect(element(by.binding('name')).getText()).toBe('world'); - }); - </file> - </example> - */ -var ngBindDirective = ['$compile', function($compile) { - return { - restrict: 'AC', - compile: function ngBindCompile(templateElement) { - $compile.$$addBindingClass(templateElement); - return function ngBindLink(scope, element, attr) { - $compile.$$addBindingInfo(element, attr.ngBind); - element = element[0]; - scope.$watch(attr.ngBind, function ngBindWatchAction(value) { - element.textContent = isUndefined(value) ? '' : value; - }); - }; - } - }; -}]; - - -/** - * @ngdoc directive - * @name ngBindTemplate - * - * @description - * The `ngBindTemplate` directive specifies that the element - * text content should be replaced with the interpolation of the template - * in the `ngBindTemplate` attribute. - * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` - * expressions. This directive is needed since some HTML elements - * (such as TITLE and OPTION) cannot contain SPAN elements. - * - * @element ANY - * @param {string} ngBindTemplate template of form - * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. - * - * @example - * Try it here: enter text in text box and watch the greeting change. - <example module="bindExample"> - <file name="index.html"> - <script> - angular.module('bindExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.salutation = 'Hello'; - $scope.name = 'World'; - }]); - </script> - <div ng-controller="ExampleController"> - <label>Salutation: <input type="text" ng-model="salutation"></label><br> - <label>Name: <input type="text" ng-model="name"></label><br> - <pre ng-bind-template="{{salutation}} {{name}}!"></pre> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-bind', function() { - var salutationElem = element(by.binding('salutation')); - var salutationInput = element(by.model('salutation')); - var nameInput = element(by.model('name')); - - expect(salutationElem.getText()).toBe('Hello World!'); - - salutationInput.clear(); - salutationInput.sendKeys('Greetings'); - nameInput.clear(); - nameInput.sendKeys('user'); - - expect(salutationElem.getText()).toBe('Greetings user!'); - }); - </file> - </example> - */ -var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) { - return { - compile: function ngBindTemplateCompile(templateElement) { - $compile.$$addBindingClass(templateElement); - return function ngBindTemplateLink(scope, element, attr) { - var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); - $compile.$$addBindingInfo(element, interpolateFn.expressions); - element = element[0]; - attr.$observe('ngBindTemplate', function(value) { - element.textContent = isUndefined(value) ? '' : value; - }); - }; - } - }; -}]; - - -/** - * @ngdoc directive - * @name ngBindHtml - * - * @description - * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default, - * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service. - * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link - * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize} - * in your module's dependencies, you need to include "angular-sanitize.js" in your application. - * - * You may also bypass sanitization for values you know are safe. To do so, bind to - * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example - * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}. - * - * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you - * will have an exception (instead of an exploit.) - * - * @element ANY - * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. - * - * @example - - <example module="bindHtmlExample" deps="angular-sanitize.js"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <p ng-bind-html="myHTML"></p> - </div> - </file> - - <file name="script.js"> - angular.module('bindHtmlExample', ['ngSanitize']) - .controller('ExampleController', ['$scope', function($scope) { - $scope.myHTML = - 'I am an <code>HTML</code>string with ' + - '<a href="#">links!</a> and other <em>stuff</em>'; - }]); - </file> - - <file name="protractor.js" type="protractor"> - it('should check ng-bind-html', function() { - expect(element(by.binding('myHTML')).getText()).toBe( - 'I am an HTMLstring with links! and other stuff'); - }); - </file> - </example> - */ -var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) { - return { - restrict: 'A', - compile: function ngBindHtmlCompile(tElement, tAttrs) { - var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml); - var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) { - return (value || '').toString(); - }); - $compile.$$addBindingClass(tElement); - - return function ngBindHtmlLink(scope, element, attr) { - $compile.$$addBindingInfo(element, attr.ngBindHtml); - - scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() { - // we re-evaluate the expr because we want a TrustedValueHolderType - // for $sce, not a string - element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || ''); - }); - }; - } - }; -}]; - -/** - * @ngdoc directive - * @name ngChange - * - * @description - * Evaluate the given expression when the user changes the input. - * The expression is evaluated immediately, unlike the JavaScript onchange event - * which only triggers at the end of a change (usually, when the user leaves the - * form element or presses the return key). - * - * The `ngChange` expression is only evaluated when a change in the input value causes - * a new value to be committed to the model. - * - * It will not be evaluated: - * * if the value returned from the `$parsers` transformation pipeline has not changed - * * if the input has continued to be invalid since the model will stay `null` - * * if the model is changed programmatically and not by a change to the input value - * - * - * Note, this directive requires `ngModel` to be present. - * - * @element input - * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change - * in input value. - * - * @example - * <example name="ngChange-directive" module="changeExample"> - * <file name="index.html"> - * <script> - * angular.module('changeExample', []) - * .controller('ExampleController', ['$scope', function($scope) { - * $scope.counter = 0; - * $scope.change = function() { - * $scope.counter++; - * }; - * }]); - * </script> - * <div ng-controller="ExampleController"> - * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> - * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> - * <label for="ng-change-example2">Confirmed</label><br /> - * <tt>debug = {{confirmed}}</tt><br/> - * <tt>counter = {{counter}}</tt><br/> - * </div> - * </file> - * <file name="protractor.js" type="protractor"> - * var counter = element(by.binding('counter')); - * var debug = element(by.binding('confirmed')); - * - * it('should evaluate the expression if changing from view', function() { - * expect(counter.getText()).toContain('0'); - * - * element(by.id('ng-change-example1')).click(); - * - * expect(counter.getText()).toContain('1'); - * expect(debug.getText()).toContain('true'); - * }); - * - * it('should not evaluate the expression if changing from model', function() { - * element(by.id('ng-change-example2')).click(); - - * expect(counter.getText()).toContain('0'); - * expect(debug.getText()).toContain('true'); - * }); - * </file> - * </example> - */ -var ngChangeDirective = valueFn({ - restrict: 'A', - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - ctrl.$viewChangeListeners.push(function() { - scope.$eval(attr.ngChange); - }); - } -}); - -function classDirective(name, selector) { - name = 'ngClass' + name; - return ['$animate', function($animate) { - return { - restrict: 'AC', - link: function(scope, element, attr) { - var oldVal; - - scope.$watch(attr[name], ngClassWatchAction, true); - - attr.$observe('class', function(value) { - ngClassWatchAction(scope.$eval(attr[name])); - }); - - - if (name !== 'ngClass') { - scope.$watch('$index', function($index, old$index) { - // jshint bitwise: false - var mod = $index & 1; - if (mod !== (old$index & 1)) { - var classes = arrayClasses(scope.$eval(attr[name])); - mod === selector ? - addClasses(classes) : - removeClasses(classes); - } - }); - } - - function addClasses(classes) { - var newClasses = digestClassCounts(classes, 1); - attr.$addClass(newClasses); - } - - function removeClasses(classes) { - var newClasses = digestClassCounts(classes, -1); - attr.$removeClass(newClasses); - } - - function digestClassCounts(classes, count) { - // Use createMap() to prevent class assumptions involving property - // names in Object.prototype - var classCounts = element.data('$classCounts') || createMap(); - var classesToUpdate = []; - forEach(classes, function(className) { - if (count > 0 || classCounts[className]) { - classCounts[className] = (classCounts[className] || 0) + count; - if (classCounts[className] === +(count > 0)) { - classesToUpdate.push(className); - } - } - }); - element.data('$classCounts', classCounts); - return classesToUpdate.join(' '); - } - - function updateClasses(oldClasses, newClasses) { - var toAdd = arrayDifference(newClasses, oldClasses); - var toRemove = arrayDifference(oldClasses, newClasses); - toAdd = digestClassCounts(toAdd, 1); - toRemove = digestClassCounts(toRemove, -1); - if (toAdd && toAdd.length) { - $animate.addClass(element, toAdd); - } - if (toRemove && toRemove.length) { - $animate.removeClass(element, toRemove); - } - } - - function ngClassWatchAction(newVal) { - if (selector === true || scope.$index % 2 === selector) { - var newClasses = arrayClasses(newVal || []); - if (!oldVal) { - addClasses(newClasses); - } else if (!equals(newVal,oldVal)) { - var oldClasses = arrayClasses(oldVal); - updateClasses(oldClasses, newClasses); - } - } - oldVal = shallowCopy(newVal); - } - } - }; - - function arrayDifference(tokens1, tokens2) { - var values = []; - - outer: - for (var i = 0; i < tokens1.length; i++) { - var token = tokens1[i]; - for (var j = 0; j < tokens2.length; j++) { - if (token == tokens2[j]) continue outer; - } - values.push(token); - } - return values; - } - - function arrayClasses(classVal) { - var classes = []; - if (isArray(classVal)) { - forEach(classVal, function(v) { - classes = classes.concat(arrayClasses(v)); - }); - return classes; - } else if (isString(classVal)) { - return classVal.split(' '); - } else if (isObject(classVal)) { - forEach(classVal, function(v, k) { - if (v) { - classes = classes.concat(k.split(' ')); - } - }); - return classes; - } - return classVal; - } - }]; -} - -/** - * @ngdoc directive - * @name ngClass - * @restrict AC - * - * @description - * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding - * an expression that represents all classes to be added. - * - * The directive operates in three different ways, depending on which of three types the expression - * evaluates to: - * - * 1. If the expression evaluates to a string, the string should be one or more space-delimited class - * names. - * - * 2. If the expression evaluates to an object, then for each key-value pair of the - * object with a truthy value the corresponding key is used as a class name. - * - * 3. If the expression evaluates to an array, each element of the array should either be a string as in - * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array - * to give you more control over what CSS classes appear. See the code below for an example of this. - * - * - * The directive won't add duplicate classes if a particular class was already set. - * - * When the expression changes, the previously added classes are removed and only then are the - * new classes added. - * - * @animations - * **add** - happens just before the class is applied to the elements - * - * **remove** - happens just before the class is removed from the element - * - * @element ANY - * @param {expression} ngClass {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class - * names, an array, or a map of class names to boolean values. In the case of a map, the - * names of the properties whose values are truthy will be added as css classes to the - * element. - * - * @example Example that demonstrates basic bindings via ngClass directive. - <example> - <file name="index.html"> - <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p> - <label> - <input type="checkbox" ng-model="deleted"> - deleted (apply "strike" class) - </label><br> - <label> - <input type="checkbox" ng-model="important"> - important (apply "bold" class) - </label><br> - <label> - <input type="checkbox" ng-model="error"> - error (apply "has-error" class) - </label> - <hr> - <p ng-class="style">Using String Syntax</p> - <input type="text" ng-model="style" - placeholder="Type: bold strike red" aria-label="Type: bold strike red"> - <hr> - <p ng-class="[style1, style2, style3]">Using Array Syntax</p> - <input ng-model="style1" - placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br> - <input ng-model="style2" - placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br> - <input ng-model="style3" - placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br> - <hr> - <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p> - <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br> - <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label> - </file> - <file name="style.css"> - .strike { - text-decoration: line-through; - } - .bold { - font-weight: bold; - } - .red { - color: red; - } - .has-error { - color: red; - background-color: yellow; - } - .orange { - color: orange; - } - </file> - <file name="protractor.js" type="protractor"> - var ps = element.all(by.css('p')); - - it('should let you toggle the class', function() { - - expect(ps.first().getAttribute('class')).not.toMatch(/bold/); - expect(ps.first().getAttribute('class')).not.toMatch(/has-error/); - - element(by.model('important')).click(); - expect(ps.first().getAttribute('class')).toMatch(/bold/); - - element(by.model('error')).click(); - expect(ps.first().getAttribute('class')).toMatch(/has-error/); - }); - - it('should let you toggle string example', function() { - expect(ps.get(1).getAttribute('class')).toBe(''); - element(by.model('style')).clear(); - element(by.model('style')).sendKeys('red'); - expect(ps.get(1).getAttribute('class')).toBe('red'); - }); - - it('array example should have 3 classes', function() { - expect(ps.get(2).getAttribute('class')).toBe(''); - element(by.model('style1')).sendKeys('bold'); - element(by.model('style2')).sendKeys('strike'); - element(by.model('style3')).sendKeys('red'); - expect(ps.get(2).getAttribute('class')).toBe('bold strike red'); - }); - - it('array with map example should have 2 classes', function() { - expect(ps.last().getAttribute('class')).toBe(''); - element(by.model('style4')).sendKeys('bold'); - element(by.model('warning')).click(); - expect(ps.last().getAttribute('class')).toBe('bold orange'); - }); - </file> - </example> - - ## Animations - - The example below demonstrates how to perform animations using ngClass. - - <example module="ngAnimate" deps="angular-animate.js" animations="true"> - <file name="index.html"> - <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'"> - <input id="clearbtn" type="button" value="clear" ng-click="myVar=''"> - <br> - <span class="base-class" ng-class="myVar">Sample Text</span> - </file> - <file name="style.css"> - .base-class { - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - } - - .base-class.my-class { - color: red; - font-size:3em; - } - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-class', function() { - expect(element(by.css('.base-class')).getAttribute('class')).not. - toMatch(/my-class/); - - element(by.id('setbtn')).click(); - - expect(element(by.css('.base-class')).getAttribute('class')). - toMatch(/my-class/); - - element(by.id('clearbtn')).click(); - - expect(element(by.css('.base-class')).getAttribute('class')).not. - toMatch(/my-class/); - }); - </file> - </example> - - - ## ngClass and pre-existing CSS3 Transitions/Animations - The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. - Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder - any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure - to view the step by step details of {@link $animate#addClass $animate.addClass} and - {@link $animate#removeClass $animate.removeClass}. - */ -var ngClassDirective = classDirective('', true); - -/** - * @ngdoc directive - * @name ngClassOdd - * @restrict AC - * - * @description - * The `ngClassOdd` and `ngClassEven` directives work exactly as - * {@link ng.directive:ngClass ngClass}, except they work in - * conjunction with `ngRepeat` and take effect only on odd (even) rows. - * - * This directive can be applied only within the scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class names or an array. - * - * @example - <example> - <file name="index.html"> - <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> - <li ng-repeat="name in names"> - <span ng-class-odd="'odd'" ng-class-even="'even'"> - {{name}} - </span> - </li> - </ol> - </file> - <file name="style.css"> - .odd { - color: red; - } - .even { - color: blue; - } - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-class-odd and ng-class-even', function() { - expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). - toMatch(/odd/); - expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). - toMatch(/even/); - }); - </file> - </example> - */ -var ngClassOddDirective = classDirective('Odd', 0); - -/** - * @ngdoc directive - * @name ngClassEven - * @restrict AC - * - * @description - * The `ngClassOdd` and `ngClassEven` directives work exactly as - * {@link ng.directive:ngClass ngClass}, except they work in - * conjunction with `ngRepeat` and take effect only on odd (even) rows. - * - * This directive can be applied only within the scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The - * result of the evaluation can be a string representing space delimited class names or an array. - * - * @example - <example> - <file name="index.html"> - <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> - <li ng-repeat="name in names"> - <span ng-class-odd="'odd'" ng-class-even="'even'"> - {{name}} - </span> - </li> - </ol> - </file> - <file name="style.css"> - .odd { - color: red; - } - .even { - color: blue; - } - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-class-odd and ng-class-even', function() { - expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). - toMatch(/odd/); - expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). - toMatch(/even/); - }); - </file> - </example> - */ -var ngClassEvenDirective = classDirective('Even', 1); - -/** - * @ngdoc directive - * @name ngCloak - * @restrict AC - * - * @description - * The `ngCloak` directive is used to prevent the Angular html template from being briefly - * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this - * directive to avoid the undesirable flicker effect caused by the html template display. - * - * The directive can be applied to the `<body>` element, but the preferred usage is to apply - * multiple `ngCloak` directives to small portions of the page to permit progressive rendering - * of the browser view. - * - * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and - * `angular.min.js`. - * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). - * - * ```css - * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { - * display: none !important; - * } - * ``` - * - * When this css rule is loaded by the browser, all html elements (including their children) that - * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive - * during the compilation of the template it deletes the `ngCloak` element attribute, making - * the compiled element visible. - * - * For the best result, the `angular.js` script must be loaded in the head section of the html - * document; alternatively, the css rule above must be included in the external stylesheet of the - * application. - * - * @element ANY - * - * @example - <example> - <file name="index.html"> - <div id="template1" ng-cloak>{{ 'hello' }}</div> - <div id="template2" class="ng-cloak">{{ 'world' }}</div> - </file> - <file name="protractor.js" type="protractor"> - it('should remove the template directive and css class', function() { - expect($('#template1').getAttribute('ng-cloak')). - toBeNull(); - expect($('#template2').getAttribute('ng-cloak')). - toBeNull(); - }); - </file> - </example> - * - */ -var ngCloakDirective = ngDirective({ - compile: function(element, attr) { - attr.$set('ngCloak', undefined); - element.removeClass('ng-cloak'); - } -}); - -/** - * @ngdoc directive - * @name ngController - * - * @description - * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular - * supports the principles behind the Model-View-Controller design pattern. - * - * MVC components in angular: - * - * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties - * are accessed through bindings. - * * View — The template (HTML with data bindings) that is rendered into the View. - * * Controller — The `ngController` directive specifies a Controller class; the class contains business - * logic behind the application to decorate the scope with functions and values - * - * Note that you can also attach controllers to the DOM by declaring it in a route definition - * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller - * again using `ng-controller` in the template itself. This will cause the controller to be attached - * and executed twice. - * - * @element ANY - * @scope - * @priority 500 - * @param {expression} ngController Name of a constructor function registered with the current - * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression} - * that on the current scope evaluates to a constructor function. - * - * The controller instance can be published into a scope property by specifying - * `ng-controller="as propertyName"`. - * - * If the current `$controllerProvider` is configured to use globals (via - * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may - * also be the name of a globally accessible constructor function (not recommended). - * - * @example - * Here is a simple form for editing user contact information. Adding, removing, clearing, and - * greeting are methods declared on the controller (see source tab). These methods can - * easily be called from the angular markup. Any changes to the data are automatically reflected - * in the View without the need for a manual update. - * - * Two different declaration styles are included below: - * - * * one binds methods and properties directly onto the controller using `this`: - * `ng-controller="SettingsController1 as settings"` - * * one injects `$scope` into the controller: - * `ng-controller="SettingsController2"` - * - * The second option is more common in the Angular community, and is generally used in boilerplates - * and in this guide. However, there are advantages to binding properties directly to the controller - * and avoiding scope. - * - * * Using `controller as` makes it obvious which controller you are accessing in the template when - * multiple controllers apply to an element. - * * If you are writing your controllers as classes you have easier access to the properties and - * methods, which will appear on the scope, from inside the controller code. - * * Since there is always a `.` in the bindings, you don't have to worry about prototypal - * inheritance masking primitives. - * - * This example demonstrates the `controller as` syntax. - * - * <example name="ngControllerAs" module="controllerAsExample"> - * <file name="index.html"> - * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings"> - * <label>Name: <input type="text" ng-model="settings.name"/></label> - * <button ng-click="settings.greet()">greet</button><br/> - * Contact: - * <ul> - * <li ng-repeat="contact in settings.contacts"> - * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}"> - * <option>phone</option> - * <option>email</option> - * </select> - * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" /> - * <button ng-click="settings.clearContact(contact)">clear</button> - * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button> - * </li> - * <li><button ng-click="settings.addContact()">add</button></li> - * </ul> - * </div> - * </file> - * <file name="app.js"> - * angular.module('controllerAsExample', []) - * .controller('SettingsController1', SettingsController1); - * - * function SettingsController1() { - * this.name = "John Smith"; - * this.contacts = [ - * {type: 'phone', value: '408 555 1212'}, - * {type: 'email', value: 'john.smith@example.org'} ]; - * } - * - * SettingsController1.prototype.greet = function() { - * alert(this.name); - * }; - * - * SettingsController1.prototype.addContact = function() { - * this.contacts.push({type: 'email', value: 'yourname@example.org'}); - * }; - * - * SettingsController1.prototype.removeContact = function(contactToRemove) { - * var index = this.contacts.indexOf(contactToRemove); - * this.contacts.splice(index, 1); - * }; - * - * SettingsController1.prototype.clearContact = function(contact) { - * contact.type = 'phone'; - * contact.value = ''; - * }; - * </file> - * <file name="protractor.js" type="protractor"> - * it('should check controller as', function() { - * var container = element(by.id('ctrl-as-exmpl')); - * expect(container.element(by.model('settings.name')) - * .getAttribute('value')).toBe('John Smith'); - * - * var firstRepeat = - * container.element(by.repeater('contact in settings.contacts').row(0)); - * var secondRepeat = - * container.element(by.repeater('contact in settings.contacts').row(1)); - * - * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe('408 555 1212'); - * - * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe('john.smith@example.org'); - * - * firstRepeat.element(by.buttonText('clear')).click(); - * - * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe(''); - * - * container.element(by.buttonText('add')).click(); - * - * expect(container.element(by.repeater('contact in settings.contacts').row(2)) - * .element(by.model('contact.value')) - * .getAttribute('value')) - * .toBe('yourname@example.org'); - * }); - * </file> - * </example> - * - * This example demonstrates the "attach to `$scope`" style of controller. - * - * <example name="ngController" module="controllerExample"> - * <file name="index.html"> - * <div id="ctrl-exmpl" ng-controller="SettingsController2"> - * <label>Name: <input type="text" ng-model="name"/></label> - * <button ng-click="greet()">greet</button><br/> - * Contact: - * <ul> - * <li ng-repeat="contact in contacts"> - * <select ng-model="contact.type" id="select_{{$index}}"> - * <option>phone</option> - * <option>email</option> - * </select> - * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" /> - * <button ng-click="clearContact(contact)">clear</button> - * <button ng-click="removeContact(contact)">X</button> - * </li> - * <li>[ <button ng-click="addContact()">add</button> ]</li> - * </ul> - * </div> - * </file> - * <file name="app.js"> - * angular.module('controllerExample', []) - * .controller('SettingsController2', ['$scope', SettingsController2]); - * - * function SettingsController2($scope) { - * $scope.name = "John Smith"; - * $scope.contacts = [ - * {type:'phone', value:'408 555 1212'}, - * {type:'email', value:'john.smith@example.org'} ]; - * - * $scope.greet = function() { - * alert($scope.name); - * }; - * - * $scope.addContact = function() { - * $scope.contacts.push({type:'email', value:'yourname@example.org'}); - * }; - * - * $scope.removeContact = function(contactToRemove) { - * var index = $scope.contacts.indexOf(contactToRemove); - * $scope.contacts.splice(index, 1); - * }; - * - * $scope.clearContact = function(contact) { - * contact.type = 'phone'; - * contact.value = ''; - * }; - * } - * </file> - * <file name="protractor.js" type="protractor"> - * it('should check controller', function() { - * var container = element(by.id('ctrl-exmpl')); - * - * expect(container.element(by.model('name')) - * .getAttribute('value')).toBe('John Smith'); - * - * var firstRepeat = - * container.element(by.repeater('contact in contacts').row(0)); - * var secondRepeat = - * container.element(by.repeater('contact in contacts').row(1)); - * - * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe('408 555 1212'); - * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe('john.smith@example.org'); - * - * firstRepeat.element(by.buttonText('clear')).click(); - * - * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) - * .toBe(''); - * - * container.element(by.buttonText('add')).click(); - * - * expect(container.element(by.repeater('contact in contacts').row(2)) - * .element(by.model('contact.value')) - * .getAttribute('value')) - * .toBe('yourname@example.org'); - * }); - * </file> - *</example> - - */ -var ngControllerDirective = [function() { - return { - restrict: 'A', - scope: true, - controller: '@', - priority: 500 - }; -}]; - -/** - * @ngdoc directive - * @name ngCsp - * - * @element html - * @description - * - * Angular has some features that can break certain - * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules. - * - * If you intend to implement these rules then you must tell Angular not to use these features. - * - * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. - * - * - * The following rules affect Angular: - * - * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions - * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30% - * increase in the speed of evaluating Angular expressions. - * - * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular - * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). - * To make these directives work when a CSP rule is blocking inline styles, you must link to the - * `angular-csp.css` in your HTML manually. - * - * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval - * and automatically deactivates this feature in the {@link $parse} service. This autodetection, - * however, triggers a CSP error to be logged in the console: - * - * ``` - * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of - * script in the following Content Security Policy directive: "default-src 'self'". Note that - * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. - * ``` - * - * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` - * directive on an element of the HTML document that appears before the `<script>` tag that loads - * the `angular.js` file. - * - * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* - * - * You can specify which of the CSP related Angular features should be deactivated by providing - * a value for the `ng-csp` attribute. The options are as follows: - * - * * no-inline-style: this stops Angular from injecting CSS styles into the DOM - * - * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings - * - * You can use these values in the following combinations: - * - * - * * No declaration means that Angular will assume that you can do inline styles, but it will do - * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions - * of Angular. - * - * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline - * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions - * of Angular. - * - * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject - * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`. - * - * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can - * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">` - * - * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject - * styles nor use eval, which is the same as an empty: ng-csp. - * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">` - * - * @example - * This example shows how to apply the `ngCsp` directive to the `html` tag. - ```html - <!doctype html> - <html ng-app ng-csp> - ... - ... - </html> - ``` - * @example - // Note: the suffix `.csp` in the example name triggers - // csp mode in our http server! - <example name="example.csp" module="cspExample" ng-csp="true"> - <file name="index.html"> - <div ng-controller="MainController as ctrl"> - <div> - <button ng-click="ctrl.inc()" id="inc">Increment</button> - <span id="counter"> - {{ctrl.counter}} - </span> - </div> - - <div> - <button ng-click="ctrl.evil()" id="evil">Evil</button> - <span id="evilError"> - {{ctrl.evilError}} - </span> - </div> - </div> - </file> - <file name="script.js"> - angular.module('cspExample', []) - .controller('MainController', function() { - this.counter = 0; - this.inc = function() { - this.counter++; - }; - this.evil = function() { - // jshint evil:true - try { - eval('1+2'); - } catch (e) { - this.evilError = e.message; - } - }; - }); - </file> - <file name="protractor.js" type="protractor"> - var util, webdriver; - - var incBtn = element(by.id('inc')); - var counter = element(by.id('counter')); - var evilBtn = element(by.id('evil')); - var evilError = element(by.id('evilError')); - - function getAndClearSevereErrors() { - return browser.manage().logs().get('browser').then(function(browserLog) { - return browserLog.filter(function(logEntry) { - return logEntry.level.value > webdriver.logging.Level.WARNING.value; - }); - }); - } - - function clearErrors() { - getAndClearSevereErrors(); - } - - function expectNoErrors() { - getAndClearSevereErrors().then(function(filteredLog) { - expect(filteredLog.length).toEqual(0); - if (filteredLog.length) { - console.log('browser console errors: ' + util.inspect(filteredLog)); - } - }); - } - - function expectError(regex) { - getAndClearSevereErrors().then(function(filteredLog) { - var found = false; - filteredLog.forEach(function(log) { - if (log.message.match(regex)) { - found = true; - } - }); - if (!found) { - throw new Error('expected an error that matches ' + regex); - } - }); - } - - beforeEach(function() { - util = require('util'); - webdriver = require('protractor/node_modules/selenium-webdriver'); - }); - - // For now, we only test on Chrome, - // as Safari does not load the page with Protractor's injected scripts, - // and Firefox webdriver always disables content security policy (#6358) - if (browser.params.browser !== 'chrome') { - return; - } - - it('should not report errors when the page is loaded', function() { - // clear errors so we are not dependent on previous tests - clearErrors(); - // Need to reload the page as the page is already loaded when - // we come here - browser.driver.getCurrentUrl().then(function(url) { - browser.get(url); - }); - expectNoErrors(); - }); - - it('should evaluate expressions', function() { - expect(counter.getText()).toEqual('0'); - incBtn.click(); - expect(counter.getText()).toEqual('1'); - expectNoErrors(); - }); - - it('should throw and report an error when using "eval"', function() { - evilBtn.click(); - expect(evilError.getText()).toMatch(/Content Security Policy/); - expectError(/Content Security Policy/); - }); - </file> - </example> - */ - -// ngCsp is not implemented as a proper directive any more, because we need it be processed while we -// bootstrap the system (before $parse is instantiated), for this reason we just have -// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc - -/** - * @ngdoc directive - * @name ngClick - * - * @description - * The ngClick directive allows you to specify custom behavior when - * an element is clicked. - * - * @element ANY - * @priority 0 - * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon - * click. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-click="count = count + 1" ng-init="count=0"> - Increment - </button> - <span> - count: {{count}} - </span> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-click', function() { - expect(element(by.binding('count')).getText()).toMatch('0'); - element(by.css('button')).click(); - expect(element(by.binding('count')).getText()).toMatch('1'); - }); - </file> - </example> - */ -/* - * A collection of directives that allows creation of custom event handlers that are defined as - * angular expressions and are compiled and executed within the current scope. - */ -var ngEventDirectives = {}; - -// For events that might fire synchronously during DOM manipulation -// we need to execute their event handlers asynchronously using $evalAsync, -// so that they are not executed in an inconsistent state. -var forceAsyncEvents = { - 'blur': true, - 'focus': true -}; -forEach( - 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), - function(eventName) { - var directiveName = directiveNormalize('ng-' + eventName); - ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { - return { - restrict: 'A', - compile: function($element, attr) { - // We expose the powerful $event object on the scope that provides access to the Window, - // etc. that isn't protected by the fast paths in $parse. We explicitly request better - // checks at the cost of speed since event handler expressions are not executed as - // frequently as regular change detection. - var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true); - return function ngEventHandler(scope, element) { - element.on(eventName, function(event) { - var callback = function() { - fn(scope, {$event:event}); - }; - if (forceAsyncEvents[eventName] && $rootScope.$$phase) { - scope.$evalAsync(callback); - } else { - scope.$apply(callback); - } - }); - }; - } - }; - }]; - } -); - -/** - * @ngdoc directive - * @name ngDblclick - * - * @description - * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. - * - * @element ANY - * @priority 0 - * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon - * a dblclick. (The Event object is available as `$event`) - * - * @example - <example> - <file name="index.html"> - <button ng-dblclick="count = count + 1" ng-init="count=0"> - Increment (on double click) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngMousedown - * - * @description - * The ngMousedown directive allows you to specify custom behavior on mousedown event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon - * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mousedown="count = count + 1" ng-init="count=0"> - Increment (on mouse down) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngMouseup - * - * @description - * Specify custom behavior on mouseup event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon - * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mouseup="count = count + 1" ng-init="count=0"> - Increment (on mouse up) - </button> - count: {{count}} - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngMouseover - * - * @description - * Specify custom behavior on mouseover event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon - * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mouseover="count = count + 1" ng-init="count=0"> - Increment (when mouse is over) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngMouseenter - * - * @description - * Specify custom behavior on mouseenter event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon - * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mouseenter="count = count + 1" ng-init="count=0"> - Increment (when mouse enters) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngMouseleave - * - * @description - * Specify custom behavior on mouseleave event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon - * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mouseleave="count = count + 1" ng-init="count=0"> - Increment (when mouse leaves) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngMousemove - * - * @description - * Specify custom behavior on mousemove event. - * - * @element ANY - * @priority 0 - * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon - * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <button ng-mousemove="count = count + 1" ng-init="count=0"> - Increment (when mouse moves) - </button> - count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngKeydown - * - * @description - * Specify custom behavior on keydown event. - * - * @element ANY - * @priority 0 - * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon - * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) - * - * @example - <example> - <file name="index.html"> - <input ng-keydown="count = count + 1" ng-init="count=0"> - key down count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngKeyup - * - * @description - * Specify custom behavior on keyup event. - * - * @element ANY - * @priority 0 - * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon - * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) - * - * @example - <example> - <file name="index.html"> - <p>Typing in the input box below updates the key count</p> - <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}} - - <p>Typing in the input box below updates the keycode</p> - <input ng-keyup="event=$event"> - <p>event keyCode: {{ event.keyCode }}</p> - <p>event altKey: {{ event.altKey }}</p> - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngKeypress - * - * @description - * Specify custom behavior on keypress event. - * - * @element ANY - * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon - * keypress. ({@link guide/expression#-event- Event object is available as `$event`} - * and can be interrogated for keyCode, altKey, etc.) - * - * @example - <example> - <file name="index.html"> - <input ng-keypress="count = count + 1" ng-init="count=0"> - key press count: {{count}} - </file> - </example> - */ - - -/** - * @ngdoc directive - * @name ngSubmit - * - * @description - * Enables binding angular expressions to onsubmit events. - * - * Additionally it prevents the default action (which for form means sending the request to the - * server and reloading the current page), but only if the form does not contain `action`, - * `data-action`, or `x-action` attributes. - * - * <div class="alert alert-warning"> - * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and - * `ngSubmit` handlers together. See the - * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} - * for a detailed discussion of when `ngSubmit` may be triggered. - * </div> - * - * @element form - * @priority 0 - * @param {expression} ngSubmit {@link guide/expression Expression} to eval. - * ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example module="submitExample"> - <file name="index.html"> - <script> - angular.module('submitExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.list = []; - $scope.text = 'hello'; - $scope.submit = function() { - if ($scope.text) { - $scope.list.push(this.text); - $scope.text = ''; - } - }; - }]); - </script> - <form ng-submit="submit()" ng-controller="ExampleController"> - Enter text and hit enter: - <input type="text" ng-model="text" name="text" /> - <input type="submit" id="submit" value="Submit" /> - <pre>list={{list}}</pre> - </form> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-submit', function() { - expect(element(by.binding('list')).getText()).toBe('list=[]'); - element(by.css('#submit')).click(); - expect(element(by.binding('list')).getText()).toContain('hello'); - expect(element(by.model('text')).getAttribute('value')).toBe(''); - }); - it('should ignore empty strings', function() { - expect(element(by.binding('list')).getText()).toBe('list=[]'); - element(by.css('#submit')).click(); - element(by.css('#submit')).click(); - expect(element(by.binding('list')).getText()).toContain('hello'); - }); - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngFocus - * - * @description - * Specify custom behavior on focus event. - * - * Note: As the `focus` event is executed synchronously when calling `input.focus()` - * AngularJS executes the expression using `scope.$evalAsync` if the event is fired - * during an `$apply` to ensure a consistent state. - * - * @element window, input, select, textarea, a - * @priority 0 - * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon - * focus. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - -/** - * @ngdoc directive - * @name ngBlur - * - * @description - * Specify custom behavior on blur event. - * - * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when - * an element has lost focus. - * - * Note: As the `blur` event is executed synchronously also during DOM manipulations - * (e.g. removing a focussed input), - * AngularJS executes the expression using `scope.$evalAsync` if the event is fired - * during an `$apply` to ensure a consistent state. - * - * @element window, input, select, textarea, a - * @priority 0 - * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon - * blur. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - -/** - * @ngdoc directive - * @name ngCopy - * - * @description - * Specify custom behavior on copy event. - * - * @element window, input, select, textarea, a - * @priority 0 - * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon - * copy. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value"> - copied: {{copied}} - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngCut - * - * @description - * Specify custom behavior on cut event. - * - * @element window, input, select, textarea, a - * @priority 0 - * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon - * cut. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value"> - cut: {{cut}} - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngPaste - * - * @description - * Specify custom behavior on paste event. - * - * @element window, input, select, textarea, a - * @priority 0 - * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon - * paste. ({@link guide/expression#-event- Event object is available as `$event`}) - * - * @example - <example> - <file name="index.html"> - <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'> - pasted: {{paste}} - </file> - </example> - */ - -/** - * @ngdoc directive - * @name ngIf - * @restrict A - * @multiElement - * - * @description - * The `ngIf` directive removes or recreates a portion of the DOM tree based on an - * {expression}. If the expression assigned to `ngIf` evaluates to a false - * value then the element is removed from the DOM, otherwise a clone of the - * element is reinserted into the DOM. - * - * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the - * element in the DOM rather than changing its visibility via the `display` css property. A common - * case when this difference is significant is when using css selectors that rely on an element's - * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes. - * - * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope - * is created when the element is restored. The scope created within `ngIf` inherits from - * its parent scope using - * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance). - * An important implication of this is if `ngModel` is used within `ngIf` to bind to - * a javascript primitive defined in the parent scope. In this case any modifications made to the - * variable within the child scope will override (hide) the value in the parent scope. - * - * Also, `ngIf` recreates elements using their compiled state. An example of this behavior - * is if an element's class attribute is directly modified after it's compiled, using something like - * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element - * the added class will be lost because the original compiled state is used to regenerate the element. - * - * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` - * and `leave` effects. - * - * @animations - * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container - * leave - happens just before the `ngIf` contents are removed from the DOM - * - * @element ANY - * @scope - * @priority 600 - * @param {expression} ngIf If the {@link guide/expression expression} is falsy then - * the element is removed from the DOM tree. If it is truthy a copy of the compiled - * element is added to the DOM tree. - * - * @example - <example module="ngAnimate" deps="angular-animate.js" animations="true"> - <file name="index.html"> - <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/> - Show when checked: - <span ng-if="checked" class="animate-if"> - This is removed when the checkbox is unchecked. - </span> - </file> - <file name="animations.css"> - .animate-if { - background:white; - border:1px solid black; - padding:10px; - } - - .animate-if.ng-enter, .animate-if.ng-leave { - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - } - - .animate-if.ng-enter, - .animate-if.ng-leave.ng-leave-active { - opacity:0; - } - - .animate-if.ng-leave, - .animate-if.ng-enter.ng-enter-active { - opacity:1; - } - </file> - </example> - */ -var ngIfDirective = ['$animate', function($animate) { - return { - multiElement: true, - transclude: 'element', - priority: 600, - terminal: true, - restrict: 'A', - $$tlb: true, - link: function($scope, $element, $attr, ctrl, $transclude) { - var block, childScope, previousElements; - $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { - - if (value) { - if (!childScope) { - $transclude(function(clone, newScope) { - childScope = newScope; - clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); - // Note: We only need the first/last node of the cloned nodes. - // However, we need to keep the reference to the jqlite wrapper as it might be changed later - // by a directive with templateUrl when its template arrives. - block = { - clone: clone - }; - $animate.enter(clone, $element.parent(), $element); - }); - } - } else { - if (previousElements) { - previousElements.remove(); - previousElements = null; - } - if (childScope) { - childScope.$destroy(); - childScope = null; - } - if (block) { - previousElements = getBlockNodes(block.clone); - $animate.leave(previousElements).then(function() { - previousElements = null; - }); - block = null; - } - } - }); - } - }; -}]; - -/** - * @ngdoc directive - * @name ngInclude - * @restrict ECA - * - * @description - * Fetches, compiles and includes an external HTML fragment. - * - * By default, the template URL is restricted to the same domain and protocol as the - * application document. This is done by calling {@link $sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols - * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or - * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link - * ng.$sce Strict Contextual Escaping}. - * - * In addition, the browser's - * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) - * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) - * policy may further restrict whether the template is successfully loaded. - * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` - * access on some browsers. - * - * @animations - * enter - animation is used to bring new content into the browser. - * leave - animation is used to animate existing content away. - * - * The enter and leave animation occur concurrently. - * - * @scope - * @priority 400 - * - * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, - * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. - * @param {string=} onload Expression to evaluate when a new partial is loaded. - * - * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll - * $anchorScroll} to scroll the viewport after the content is loaded. - * - * - If the attribute is not set, disable scrolling. - * - If the attribute is set without value, enable scrolling. - * - Otherwise enable scrolling only if the expression evaluates to truthy value. - * - * @example - <example module="includeExample" deps="angular-animate.js" animations="true"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <select ng-model="template" ng-options="t.name for t in templates"> - <option value="">(blank)</option> - </select> - url of the template: <code>{{template.url}}</code> - <hr/> - <div class="slide-animate-container"> - <div class="slide-animate" ng-include="template.url"></div> - </div> - </div> - </file> - <file name="script.js"> - angular.module('includeExample', ['ngAnimate']) - .controller('ExampleController', ['$scope', function($scope) { - $scope.templates = - [ { name: 'template1.html', url: 'template1.html'}, - { name: 'template2.html', url: 'template2.html'} ]; - $scope.template = $scope.templates[0]; - }]); - </file> - <file name="template1.html"> - Content of template1.html - </file> - <file name="template2.html"> - Content of template2.html - </file> - <file name="animations.css"> - .slide-animate-container { - position:relative; - background:white; - border:1px solid black; - height:40px; - overflow:hidden; - } - - .slide-animate { - padding:10px; - } - - .slide-animate.ng-enter, .slide-animate.ng-leave { - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - - position:absolute; - top:0; - left:0; - right:0; - bottom:0; - display:block; - padding:10px; - } - - .slide-animate.ng-enter { - top:-50px; - } - .slide-animate.ng-enter.ng-enter-active { - top:0; - } - - .slide-animate.ng-leave { - top:0; - } - .slide-animate.ng-leave.ng-leave-active { - top:50px; - } - </file> - <file name="protractor.js" type="protractor"> - var templateSelect = element(by.model('template')); - var includeElem = element(by.css('[ng-include]')); - - it('should load template1.html', function() { - expect(includeElem.getText()).toMatch(/Content of template1.html/); - }); - - it('should load template2.html', function() { - if (browser.params.browser == 'firefox') { - // Firefox can't handle using selects - // See https://github.com/angular/protractor/issues/480 - return; - } - templateSelect.click(); - templateSelect.all(by.css('option')).get(2).click(); - expect(includeElem.getText()).toMatch(/Content of template2.html/); - }); - - it('should change to blank', function() { - if (browser.params.browser == 'firefox') { - // Firefox can't handle using selects - return; - } - templateSelect.click(); - templateSelect.all(by.css('option')).get(0).click(); - expect(includeElem.isPresent()).toBe(false); - }); - </file> - </example> - */ - - -/** - * @ngdoc event - * @name ngInclude#$includeContentRequested - * @eventType emit on the scope ngInclude was declared in - * @description - * Emitted every time the ngInclude content is requested. - * - * @param {Object} angularEvent Synthetic event object. - * @param {String} src URL of content to load. - */ - - -/** - * @ngdoc event - * @name ngInclude#$includeContentLoaded - * @eventType emit on the current ngInclude scope - * @description - * Emitted every time the ngInclude content is reloaded. - * - * @param {Object} angularEvent Synthetic event object. - * @param {String} src URL of content to load. - */ - - -/** - * @ngdoc event - * @name ngInclude#$includeContentError - * @eventType emit on the scope ngInclude was declared in - * @description - * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299) - * - * @param {Object} angularEvent Synthetic event object. - * @param {String} src URL of content to load. - */ -var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', - function($templateRequest, $anchorScroll, $animate) { - return { - restrict: 'ECA', - priority: 400, - terminal: true, - transclude: 'element', - controller: angular.noop, - compile: function(element, attr) { - var srcExp = attr.ngInclude || attr.src, - onloadExp = attr.onload || '', - autoScrollExp = attr.autoscroll; - - return function(scope, $element, $attr, ctrl, $transclude) { - var changeCounter = 0, - currentScope, - previousElement, - currentElement; - - var cleanupLastIncludeContent = function() { - if (previousElement) { - previousElement.remove(); - previousElement = null; - } - if (currentScope) { - currentScope.$destroy(); - currentScope = null; - } - if (currentElement) { - $animate.leave(currentElement).then(function() { - previousElement = null; - }); - previousElement = currentElement; - currentElement = null; - } - }; - - scope.$watch(srcExp, function ngIncludeWatchAction(src) { - var afterAnimation = function() { - if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - }; - var thisChangeId = ++changeCounter; - - if (src) { - //set the 2nd param to true to ignore the template request error so that the inner - //contents and scope can be cleaned up. - $templateRequest(src, true).then(function(response) { - if (thisChangeId !== changeCounter) return; - var newScope = scope.$new(); - ctrl.template = response; - - // Note: This will also link all children of ng-include that were contained in the original - // html. If that content contains controllers, ... they could pollute/change the scope. - // However, using ng-include on an element with additional content does not make sense... - // Note: We can't remove them in the cloneAttchFn of $transclude as that - // function is called before linking the content, which would apply child - // directives to non existing elements. - var clone = $transclude(newScope, function(clone) { - cleanupLastIncludeContent(); - $animate.enter(clone, null, $element).then(afterAnimation); - }); - - currentScope = newScope; - currentElement = clone; - - currentScope.$emit('$includeContentLoaded', src); - scope.$eval(onloadExp); - }, function() { - if (thisChangeId === changeCounter) { - cleanupLastIncludeContent(); - scope.$emit('$includeContentError', src); - } - }); - scope.$emit('$includeContentRequested', src); - } else { - cleanupLastIncludeContent(); - ctrl.template = null; - } - }); - }; - } - }; -}]; - -// This directive is called during the $transclude call of the first `ngInclude` directive. -// It will replace and compile the content of the element with the loaded template. -// We need this directive so that the element content is already filled when -// the link function of another directive on the same element as ngInclude -// is called. -var ngIncludeFillContentDirective = ['$compile', - function($compile) { - return { - restrict: 'ECA', - priority: -400, - require: 'ngInclude', - link: function(scope, $element, $attr, ctrl) { - if (/SVG/.test($element[0].toString())) { - // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not - // support innerHTML, so detect this here and try to generate the contents - // specially. - $element.empty(); - $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope, - function namespaceAdaptedClone(clone) { - $element.append(clone); - }, {futureParentElement: $element}); - return; - } - - $element.html(ctrl.template); - $compile($element.contents())(scope); - } - }; - }]; - -/** - * @ngdoc directive - * @name ngInit - * @restrict AC - * - * @description - * The `ngInit` directive allows you to evaluate an expression in the - * current scope. - * - * <div class="alert alert-danger"> - * This directive can be abused to add unnecessary amounts of logic into your templates. - * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of - * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via - * server side scripting. Besides these few cases, you should use {@link guide/controller controllers} - * rather than `ngInit` to initialize values on a scope. - * </div> - * - * <div class="alert alert-warning"> - * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make - * sure you have parentheses to ensure correct operator precedence: - * <pre class="prettyprint"> - * `<div ng-init="test1 = ($index | toString)"></div>` - * </pre> - * </div> - * - * @priority 450 - * - * @element ANY - * @param {expression} ngInit {@link guide/expression Expression} to eval. - * - * @example - <example module="initExample"> - <file name="index.html"> - <script> - angular.module('initExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.list = [['a', 'b'], ['c', 'd']]; - }]); - </script> - <div ng-controller="ExampleController"> - <div ng-repeat="innerList in list" ng-init="outerIndex = $index"> - <div ng-repeat="value in innerList" ng-init="innerIndex = $index"> - <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span> - </div> - </div> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should alias index positions', function() { - var elements = element.all(by.css('.example-init')); - expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); - expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); - expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); - expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); - }); - </file> - </example> - */ -var ngInitDirective = ngDirective({ - priority: 450, - compile: function() { - return { - pre: function(scope, element, attrs) { - scope.$eval(attrs.ngInit); - } - }; - } -}); - -/** - * @ngdoc directive - * @name ngList - * - * @description - * Text input that converts between a delimited string and an array of strings. The default - * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom - * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`. - * - * The behaviour of the directive is affected by the use of the `ngTrim` attribute. - * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each - * list item is respected. This implies that the user of the directive is responsible for - * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a - * tab or newline character. - * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected - * when joining the list items back together) and whitespace around each list item is stripped - * before it is added to the model. - * - * ### Example with Validation - * - * <example name="ngList-directive" module="listExample"> - * <file name="app.js"> - * angular.module('listExample', []) - * .controller('ExampleController', ['$scope', function($scope) { - * $scope.names = ['morpheus', 'neo', 'trinity']; - * }]); - * </file> - * <file name="index.html"> - * <form name="myForm" ng-controller="ExampleController"> - * <label>List: <input name="namesInput" ng-model="names" ng-list required></label> - * <span role="alert"> - * <span class="error" ng-show="myForm.namesInput.$error.required"> - * Required!</span> - * </span> - * <br> - * <tt>names = {{names}}</tt><br/> - * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> - * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> - * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - * </form> - * </file> - * <file name="protractor.js" type="protractor"> - * var listInput = element(by.model('names')); - * var names = element(by.exactBinding('names')); - * var valid = element(by.binding('myForm.namesInput.$valid')); - * var error = element(by.css('span.error')); - * - * it('should initialize to model', function() { - * expect(names.getText()).toContain('["morpheus","neo","trinity"]'); - * expect(valid.getText()).toContain('true'); - * expect(error.getCssValue('display')).toBe('none'); - * }); - * - * it('should be invalid if empty', function() { - * listInput.clear(); - * listInput.sendKeys(''); - * - * expect(names.getText()).toContain(''); - * expect(valid.getText()).toContain('false'); - * expect(error.getCssValue('display')).not.toBe('none'); - * }); - * </file> - * </example> - * - * ### Example - splitting on newline - * <example name="ngList-directive-newlines"> - * <file name="index.html"> - * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea> - * <pre>{{ list | json }}</pre> - * </file> - * <file name="protractor.js" type="protractor"> - * it("should split the text by newlines", function() { - * var listInput = element(by.model('list')); - * var output = element(by.binding('list | json')); - * listInput.sendKeys('abc\ndef\nghi'); - * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]'); - * }); - * </file> - * </example> - * - * @element input - * @param {string=} ngList optional delimiter that should be used to split the value. - */ -var ngListDirective = function() { - return { - restrict: 'A', - priority: 100, - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - // We want to control whitespace trimming so we use this convoluted approach - // to access the ngList attribute, which doesn't pre-trim the attribute - var ngList = element.attr(attr.$attr.ngList) || ', '; - var trimValues = attr.ngTrim !== 'false'; - var separator = trimValues ? trim(ngList) : ngList; - - var parse = function(viewValue) { - // If the viewValue is invalid (say required but empty) it will be `undefined` - if (isUndefined(viewValue)) return; - - var list = []; - - if (viewValue) { - forEach(viewValue.split(separator), function(value) { - if (value) list.push(trimValues ? trim(value) : value); - }); - } - - return list; - }; - - ctrl.$parsers.push(parse); - ctrl.$formatters.push(function(value) { - if (isArray(value)) { - return value.join(ngList); - } - - return undefined; - }); - - // Override the standard $isEmpty because an empty array means the input is empty. - ctrl.$isEmpty = function(value) { - return !value || !value.length; - }; - } - }; -}; - -/* global VALID_CLASS: true, - INVALID_CLASS: true, - PRISTINE_CLASS: true, - DIRTY_CLASS: true, - UNTOUCHED_CLASS: true, - TOUCHED_CLASS: true, -*/ - -var VALID_CLASS = 'ng-valid', - INVALID_CLASS = 'ng-invalid', - PRISTINE_CLASS = 'ng-pristine', - DIRTY_CLASS = 'ng-dirty', - UNTOUCHED_CLASS = 'ng-untouched', - TOUCHED_CLASS = 'ng-touched', - PENDING_CLASS = 'ng-pending'; - -var ngModelMinErr = minErr('ngModel'); - -/** - * @ngdoc type - * @name ngModel.NgModelController - * - * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a - * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue - * is set. - * @property {*} $modelValue The value in the model that the control is bound to. - * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever - the control reads value from the DOM. The functions are called in array order, each passing - its return value through to the next. The last return value is forwarded to the - {@link ngModel.NgModelController#$validators `$validators`} collection. - -Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue -`$viewValue`}. - -Returning `undefined` from a parser means a parse error occurred. In that case, -no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` -will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} -is set to `true`. The parse error is stored in `ngModel.$error.parse`. - - * - * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever - the model value changes. The functions are called in reverse array order, each passing the value through to the - next. The last return value is used as the actual DOM value. - Used to format / convert values for display in the control. - * ```js - * function formatter(value) { - * if (value) { - * return value.toUpperCase(); - * } - * } - * ngModel.$formatters.push(formatter); - * ``` - * - * @property {Object.<string, function>} $validators A collection of validators that are applied - * whenever the model value changes. The key value within the object refers to the name of the - * validator while the function refers to the validation operation. The validation operation is - * provided with the model value as an argument and must return a true or false value depending - * on the response of that validation. - * - * ```js - * ngModel.$validators.validCharacters = function(modelValue, viewValue) { - * var value = modelValue || viewValue; - * return /[0-9]+/.test(value) && - * /[a-z]+/.test(value) && - * /[A-Z]+/.test(value) && - * /\W+/.test(value); - * }; - * ``` - * - * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to - * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided - * is expected to return a promise when it is run during the model validation process. Once the promise - * is delivered then the validation status will be set to true when fulfilled and false when rejected. - * When the asynchronous validators are triggered, each of the validators will run in parallel and the model - * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator - * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators - * will only run once all synchronous validators have passed. - * - * Please note that if $http is used then it is important that the server returns a success HTTP response code - * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. - * - * ```js - * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { - * var value = modelValue || viewValue; - * - * // Lookup user by username - * return $http.get('/api/users/' + value). - * then(function resolved() { - * //username exists, this means validation fails - * return $q.reject('exists'); - * }, function rejected() { - * //username does not exist, therefore this validation passes - * return true; - * }); - * }; - * ``` - * - * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the - * view value has changed. It is called with no arguments, and its return value is ignored. - * This can be used in place of additional $watches against the model value. - * - * @property {Object} $error An object hash with all failing validator ids as keys. - * @property {Object} $pending An object hash with all pending validator ids as keys. - * - * @property {boolean} $untouched True if control has not lost focus yet. - * @property {boolean} $touched True if control has lost focus. - * @property {boolean} $pristine True if user has not interacted with the control yet. - * @property {boolean} $dirty True if user has already interacted with the control. - * @property {boolean} $valid True if there is no error. - * @property {boolean} $invalid True if at least one error on the control. - * @property {string} $name The name attribute of the control. - * - * @description - * - * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. - * The controller contains services for data-binding, validation, CSS updates, and value formatting - * and parsing. It purposefully does not contain any logic which deals with DOM rendering or - * listening to DOM events. - * Such DOM related logic should be provided by other directives which make use of - * `NgModelController` for data-binding to control elements. - * Angular provides this DOM logic for most {@link input `input`} elements. - * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example - * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. - * - * @example - * ### Custom Control Example - * This example shows how to use `NgModelController` with a custom control to achieve - * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) - * collaborate together to achieve the desired result. - * - * `contenteditable` is an HTML5 attribute, which tells the browser to let the element - * contents be edited in place by the user. - * - * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} - * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). - * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks - * that content using the `$sce` service. - * - * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> - <file name="style.css"> - [contenteditable] { - border: 1px solid black; - background-color: white; - min-height: 20px; - } - - .ng-invalid { - border: 1px solid red; - } - - </file> - <file name="script.js"> - angular.module('customControl', ['ngSanitize']). - directive('contenteditable', ['$sce', function($sce) { - return { - restrict: 'A', // only activate on element attribute - require: '?ngModel', // get a hold of NgModelController - link: function(scope, element, attrs, ngModel) { - if (!ngModel) return; // do nothing if no ng-model - - // Specify how UI should be updated - ngModel.$render = function() { - element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); - }; - - // Listen for change events to enable binding - element.on('blur keyup change', function() { - scope.$evalAsync(read); - }); - read(); // initialize - - // Write data to the model - function read() { - var html = element.html(); - // When we clear the content editable the browser leaves a <br> behind - // If strip-br attribute is provided then we strip this out - if ( attrs.stripBr && html == '<br>' ) { - html = ''; - } - ngModel.$setViewValue(html); - } - } - }; - }]); - </file> - <file name="index.html"> - <form name="myForm"> - <div contenteditable - name="myWidget" ng-model="userContent" - strip-br="true" - required>Change me!</div> - <span ng-show="myForm.myWidget.$error.required">Required!</span> - <hr> - <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea> - </form> - </file> - <file name="protractor.js" type="protractor"> - it('should data-bind and become invalid', function() { - if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') { - // SafariDriver can't handle contenteditable - // and Firefox driver can't clear contenteditables very well - return; - } - var contentEditable = element(by.css('[contenteditable]')); - var content = 'Change me!'; - - expect(contentEditable.getText()).toEqual(content); - - contentEditable.clear(); - contentEditable.sendKeys(protractor.Key.BACK_SPACE); - expect(contentEditable.getText()).toEqual(''); - expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); - }); - </file> - * </example> - * - * - */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', - function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { - this.$viewValue = Number.NaN; - this.$modelValue = Number.NaN; - this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. - this.$validators = {}; - this.$asyncValidators = {}; - this.$parsers = []; - this.$formatters = []; - this.$viewChangeListeners = []; - this.$untouched = true; - this.$touched = false; - this.$pristine = true; - this.$dirty = false; - this.$valid = true; - this.$invalid = false; - this.$error = {}; // keep invalid keys here - this.$$success = {}; // keep valid keys here - this.$pending = undefined; // keep pending keys here - this.$name = $interpolate($attr.name || '', false)($scope); - this.$$parentForm = nullFormCtrl; - - var parsedNgModel = $parse($attr.ngModel), - parsedNgModelAssign = parsedNgModel.assign, - ngModelGet = parsedNgModel, - ngModelSet = parsedNgModelAssign, - pendingDebounce = null, - parserValid, - ctrl = this; - - this.$$setOptions = function(options) { - ctrl.$options = options; - if (options && options.getterSetter) { - var invokeModelGetter = $parse($attr.ngModel + '()'), - invokeModelSetter = $parse($attr.ngModel + '($$$p)'); - - ngModelGet = function($scope) { - var modelValue = parsedNgModel($scope); - if (isFunction(modelValue)) { - modelValue = invokeModelGetter($scope); - } - return modelValue; - }; - ngModelSet = function($scope, newValue) { - if (isFunction(parsedNgModel($scope))) { - invokeModelSetter($scope, {$$$p: ctrl.$modelValue}); - } else { - parsedNgModelAssign($scope, ctrl.$modelValue); - } - }; - } else if (!parsedNgModel.assign) { - throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}", - $attr.ngModel, startingTag($element)); - } - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$render - * - * @description - * Called when the view needs to be updated. It is expected that the user of the ng-model - * directive will implement this method. - * - * The `$render()` method is invoked in the following situations: - * - * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last - * committed value then `$render()` is called to update the input control. - * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and - * the `$viewValue` are different from last time. - * - * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of - * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue` - * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be - * invoked if you only change a property on the objects. - */ - this.$render = noop; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$isEmpty - * - * @description - * This is called when we need to determine if the value of an input is empty. - * - * For instance, the required directive does this to work out if the input has data or not. - * - * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. - * - * You can override this for input directives whose concept of being empty is different from the - * default. The `checkboxInputType` directive does this because in its case a value of `false` - * implies empty. - * - * @param {*} value The value of the input to check for emptiness. - * @returns {boolean} True if `value` is "empty". - */ - this.$isEmpty = function(value) { - return isUndefined(value) || value === '' || value === null || value !== value; - }; - - var currentValidationRunId = 0; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setValidity - * - * @description - * Change the validity state, and notify the form. - * - * This method can be called within $parsers/$formatters or a custom validation implementation. - * However, in most cases it should be sufficient to use the `ngModel.$validators` and - * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically. - * - * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned - * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` - * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. - * The `validationErrorKey` should be in camelCase and will get converted into dash-case - * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` - * class and can be bound to as `{{someForm.someControl.$error.myError}}` . - * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), - * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. - * Skipped is used by Angular when validators do not run because of parse errors and - * when `$asyncValidators` do not run because any of the `$validators` failed. - */ - addSetValidityMethod({ - ctrl: this, - $element: $element, - set: function(object, property) { - object[property] = true; - }, - unset: function(object, property) { - delete object[property]; - }, - $animate: $animate - }); - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setPristine - * - * @description - * Sets the control to its pristine state. - * - * This method can be called to remove the `ng-dirty` class and set the control to its pristine - * state (`ng-pristine` class). A model is considered to be pristine when the control - * has not been changed from when first compiled. - */ - this.$setPristine = function() { - ctrl.$dirty = false; - ctrl.$pristine = true; - $animate.removeClass($element, DIRTY_CLASS); - $animate.addClass($element, PRISTINE_CLASS); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setDirty - * - * @description - * Sets the control to its dirty state. - * - * This method can be called to remove the `ng-pristine` class and set the control to its dirty - * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed - * from when first compiled. - */ - this.$setDirty = function() { - ctrl.$dirty = true; - ctrl.$pristine = false; - $animate.removeClass($element, PRISTINE_CLASS); - $animate.addClass($element, DIRTY_CLASS); - ctrl.$$parentForm.$setDirty(); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setUntouched - * - * @description - * Sets the control to its untouched state. - * - * This method can be called to remove the `ng-touched` class and set the control to its - * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched - * by default, however this function can be used to restore that state if the model has - * already been touched by the user. - */ - this.$setUntouched = function() { - ctrl.$touched = false; - ctrl.$untouched = true; - $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setTouched - * - * @description - * Sets the control to its touched state. - * - * This method can be called to remove the `ng-untouched` class and set the control to its - * touched state (`ng-touched` class). A model is considered to be touched when the user has - * first focused the control element and then shifted focus away from the control (blur event). - */ - this.$setTouched = function() { - ctrl.$touched = true; - ctrl.$untouched = false; - $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$rollbackViewValue - * - * @description - * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`, - * which may be caused by a pending debounced event or because the input is waiting for a some - * future event. - * - * If you have an input that uses `ng-model-options` to set up debounced events or events such - * as blur you can have a situation where there is a period when the `$viewValue` - * is out of synch with the ngModel's `$modelValue`. - * - * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue` - * programmatically before these debounced/future events have resolved/occurred, because Angular's - * dirty checking mechanism is not able to tell whether the model has actually changed or not. - * - * The `$rollbackViewValue()` method should be called before programmatically changing the model of an - * input which may have such events pending. This is important in order to make sure that the - * input field will be updated with the new model value and any pending operations are cancelled. - * - * <example name="ng-model-cancel-update" module="cancel-update-example"> - * <file name="app.js"> - * angular.module('cancel-update-example', []) - * - * .controller('CancelUpdateController', ['$scope', function($scope) { - * $scope.resetWithCancel = function(e) { - * if (e.keyCode == 27) { - * $scope.myForm.myInput1.$rollbackViewValue(); - * $scope.myValue = ''; - * } - * }; - * $scope.resetWithoutCancel = function(e) { - * if (e.keyCode == 27) { - * $scope.myValue = ''; - * } - * }; - * }]); - * </file> - * <file name="index.html"> - * <div ng-controller="CancelUpdateController"> - * <p>Try typing something in each input. See that the model only updates when you - * blur off the input. - * </p> - * <p>Now see what happens if you start typing then press the Escape key</p> - * - * <form name="myForm" ng-model-options="{ updateOn: 'blur' }"> - * <p id="inputDescription1">With $rollbackViewValue()</p> - * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue" - * ng-keydown="resetWithCancel($event)"><br/> - * myValue: "{{ myValue }}" - * - * <p id="inputDescription2">Without $rollbackViewValue()</p> - * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue" - * ng-keydown="resetWithoutCancel($event)"><br/> - * myValue: "{{ myValue }}" - * </form> - * </div> - * </file> - * </example> - */ - this.$rollbackViewValue = function() { - $timeout.cancel(pendingDebounce); - ctrl.$viewValue = ctrl.$$lastCommittedViewValue; - ctrl.$render(); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$validate - * - * @description - * Runs each of the registered validators (first synchronous validators and then - * asynchronous validators). - * If the validity changes to invalid, the model will be set to `undefined`, - * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. - * If the validity changes to valid, it will set the model to the last available valid - * `$modelValue`, i.e. either the last parsed value or the last value set from the scope. - */ - this.$validate = function() { - // ignore $validate before model is initialized - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { - return; - } - - var viewValue = ctrl.$$lastCommittedViewValue; - // Note: we use the $$rawModelValue as $modelValue might have been - // set to undefined during a view -> model update that found validation - // errors. We can't parse the view here, since that could change - // the model although neither viewValue nor the model on the scope changed - var modelValue = ctrl.$$rawModelValue; - - var prevValid = ctrl.$valid; - var prevModelValue = ctrl.$modelValue; - - var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; - - ctrl.$$runValidators(modelValue, viewValue, function(allValid) { - // If there was no change in validity, don't update the model - // This prevents changing an invalid modelValue to undefined - if (!allowInvalid && prevValid !== allValid) { - // Note: Don't check ctrl.$valid here, as we could have - // external validators (e.g. calculated on the server), - // that just call $setValidity and need the model value - // to calculate their validity. - ctrl.$modelValue = allValid ? modelValue : undefined; - - if (ctrl.$modelValue !== prevModelValue) { - ctrl.$$writeModelToScope(); - } - } - }); - - }; - - this.$$runValidators = function(modelValue, viewValue, doneCallback) { - currentValidationRunId++; - var localValidationRunId = currentValidationRunId; - - // check parser error - if (!processParseErrors()) { - validationDone(false); - return; - } - if (!processSyncValidators()) { - validationDone(false); - return; - } - processAsyncValidators(); - - function processParseErrors() { - var errorKey = ctrl.$$parserName || 'parse'; - if (isUndefined(parserValid)) { - setValidity(errorKey, null); - } else { - if (!parserValid) { - forEach(ctrl.$validators, function(v, name) { - setValidity(name, null); - }); - forEach(ctrl.$asyncValidators, function(v, name) { - setValidity(name, null); - }); - } - // Set the parse error last, to prevent unsetting it, should a $validators key == parserName - setValidity(errorKey, parserValid); - return parserValid; - } - return true; - } - - function processSyncValidators() { - var syncValidatorsValid = true; - forEach(ctrl.$validators, function(validator, name) { - var result = validator(modelValue, viewValue); - syncValidatorsValid = syncValidatorsValid && result; - setValidity(name, result); - }); - if (!syncValidatorsValid) { - forEach(ctrl.$asyncValidators, function(v, name) { - setValidity(name, null); - }); - return false; - } - return true; - } - - function processAsyncValidators() { - var validatorPromises = []; - var allValid = true; - forEach(ctrl.$asyncValidators, function(validator, name) { - var promise = validator(modelValue, viewValue); - if (!isPromiseLike(promise)) { - throw ngModelMinErr("$asyncValidators", - "Expected asynchronous validator to return a promise but got '{0}' instead.", promise); - } - setValidity(name, undefined); - validatorPromises.push(promise.then(function() { - setValidity(name, true); - }, function(error) { - allValid = false; - setValidity(name, false); - })); - }); - if (!validatorPromises.length) { - validationDone(true); - } else { - $q.all(validatorPromises).then(function() { - validationDone(allValid); - }, noop); - } - } - - function setValidity(name, isValid) { - if (localValidationRunId === currentValidationRunId) { - ctrl.$setValidity(name, isValid); - } - } - - function validationDone(allValid) { - if (localValidationRunId === currentValidationRunId) { - - doneCallback(allValid); - } - } - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$commitViewValue - * - * @description - * Commit a pending update to the `$modelValue`. - * - * Updates may be pending by a debounced event or because the input is waiting for a some future - * event defined in `ng-model-options`. this method is rarely needed as `NgModelController` - * usually handles calling this in response to input events. - */ - this.$commitViewValue = function() { - var viewValue = ctrl.$viewValue; - - $timeout.cancel(pendingDebounce); - - // If the view value has not changed then we should just exit, except in the case where there is - // a native validator on the element. In this case the validation state may have changed even though - // the viewValue has stayed empty. - if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) { - return; - } - ctrl.$$lastCommittedViewValue = viewValue; - - // change to dirty - if (ctrl.$pristine) { - this.$setDirty(); - } - this.$$parseAndValidate(); - }; - - this.$$parseAndValidate = function() { - var viewValue = ctrl.$$lastCommittedViewValue; - var modelValue = viewValue; - parserValid = isUndefined(modelValue) ? undefined : true; - - if (parserValid) { - for (var i = 0; i < ctrl.$parsers.length; i++) { - modelValue = ctrl.$parsers[i](modelValue); - if (isUndefined(modelValue)) { - parserValid = false; - break; - } - } - } - if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { - // ctrl.$modelValue has not been touched yet... - ctrl.$modelValue = ngModelGet($scope); - } - var prevModelValue = ctrl.$modelValue; - var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; - ctrl.$$rawModelValue = modelValue; - - if (allowInvalid) { - ctrl.$modelValue = modelValue; - writeToModelIfNeeded(); - } - - // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date. - // This can happen if e.g. $setViewValue is called from inside a parser - ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) { - if (!allowInvalid) { - // Note: Don't check ctrl.$valid here, as we could have - // external validators (e.g. calculated on the server), - // that just call $setValidity and need the model value - // to calculate their validity. - ctrl.$modelValue = allValid ? modelValue : undefined; - writeToModelIfNeeded(); - } - }); - - function writeToModelIfNeeded() { - if (ctrl.$modelValue !== prevModelValue) { - ctrl.$$writeModelToScope(); - } - } - }; - - this.$$writeModelToScope = function() { - ngModelSet($scope, ctrl.$modelValue); - forEach(ctrl.$viewChangeListeners, function(listener) { - try { - listener(); - } catch (e) { - $exceptionHandler(e); - } - }); - }; - - /** - * @ngdoc method - * @name ngModel.NgModelController#$setViewValue - * - * @description - * Update the view value. - * - * This method should be called when a control wants to change the view value; typically, - * this is done from within a DOM event handler. For example, the {@link ng.directive:input input} - * directive calls it when the value of the input changes and {@link ng.directive:select select} - * calls it when an option is selected. - * - * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers` - * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged - * value sent directly for processing, finally to be applied to `$modelValue` and then the - * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners, - * in the `$viewChangeListeners` list, are called. - * - * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn` - * and the `default` trigger is not listed, all those actions will remain pending until one of the - * `updateOn` events is triggered on the DOM element. - * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions} - * directive is used with a custom debounce for this particular event. - * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce` - * is specified, once the timer runs out. - * - * When used with standard inputs, the view value will always be a string (which is in some cases - * parsed into another type, such as a `Date` object for `input[date]`.) - * However, custom controls might also pass objects to this method. In this case, we should make - * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not - * perform a deep watch of objects, it only looks for a change of identity. If you only change - * the property of the object then ngModel will not realise that the object has changed and - * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should - * not change properties of the copy once it has been passed to `$setViewValue`. - * Otherwise you may cause the model value on the scope to change incorrectly. - * - * <div class="alert alert-info"> - * In any case, the value passed to the method should always reflect the current value - * of the control. For example, if you are calling `$setViewValue` for an input element, - * you should pass the input DOM value. Otherwise, the control and the scope model become - * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change - * the control's DOM value in any way. If we want to change the control's DOM value - * programmatically, we should update the `ngModel` scope expression. Its new value will be - * picked up by the model controller, which will run it through the `$formatters`, `$render` it - * to update the DOM, and finally call `$validate` on it. - * </div> - * - * @param {*} value value from the view. - * @param {string} trigger Event that triggered the update. - */ - this.$setViewValue = function(value, trigger) { - ctrl.$viewValue = value; - if (!ctrl.$options || ctrl.$options.updateOnDefault) { - ctrl.$$debounceViewValueCommit(trigger); - } - }; - - this.$$debounceViewValueCommit = function(trigger) { - var debounceDelay = 0, - options = ctrl.$options, - debounce; - - if (options && isDefined(options.debounce)) { - debounce = options.debounce; - if (isNumber(debounce)) { - debounceDelay = debounce; - } else if (isNumber(debounce[trigger])) { - debounceDelay = debounce[trigger]; - } else if (isNumber(debounce['default'])) { - debounceDelay = debounce['default']; - } - } - - $timeout.cancel(pendingDebounce); - if (debounceDelay) { - pendingDebounce = $timeout(function() { - ctrl.$commitViewValue(); - }, debounceDelay); - } else if ($rootScope.$$phase) { - ctrl.$commitViewValue(); - } else { - $scope.$apply(function() { - ctrl.$commitViewValue(); - }); - } - }; - - // model -> value - // Note: we cannot use a normal scope.$watch as we want to detect the following: - // 1. scope value is 'a' - // 2. user enters 'b' - // 3. ng-change kicks in and reverts scope value to 'a' - // -> scope value did not change since the last digest as - // ng-change executes in apply phase - // 4. view should be changed back to 'a' - $scope.$watch(function ngModelWatch() { - var modelValue = ngModelGet($scope); - - // if scope model value and ngModel value are out of sync - // TODO(perf): why not move this to the action fn? - if (modelValue !== ctrl.$modelValue && - // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator - (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) - ) { - ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; - parserValid = undefined; - - var formatters = ctrl.$formatters, - idx = formatters.length; - - var viewValue = modelValue; - while (idx--) { - viewValue = formatters[idx](viewValue); - } - if (ctrl.$viewValue !== viewValue) { - ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; - ctrl.$render(); - - ctrl.$$runValidators(modelValue, viewValue, noop); - } - } - - return modelValue; - }); -}]; - - -/** - * @ngdoc directive - * @name ngModel - * - * @element input - * @priority 1 - * - * @description - * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a - * property on the scope using {@link ngModel.NgModelController NgModelController}, - * which is created and exposed by this directive. - * - * `ngModel` is responsible for: - * - * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` - * require. - * - Providing validation behavior (i.e. required, number, email, url). - * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors). - * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations. - * - Registering the control with its parent {@link ng.directive:form form}. - * - * Note: `ngModel` will try to bind to the property given by evaluating the expression on the - * current scope. If the property doesn't already exist on this scope, it will be created - * implicitly and added to the scope. - * - * For best practices on using `ngModel`, see: - * - * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) - * - * For basic examples, how to use `ngModel`, see: - * - * - {@link ng.directive:input input} - * - {@link input[text] text} - * - {@link input[checkbox] checkbox} - * - {@link input[radio] radio} - * - {@link input[number] number} - * - {@link input[email] email} - * - {@link input[url] url} - * - {@link input[date] date} - * - {@link input[datetime-local] datetime-local} - * - {@link input[time] time} - * - {@link input[month] month} - * - {@link input[week] week} - * - {@link ng.directive:select select} - * - {@link ng.directive:textarea textarea} - * - * # CSS classes - * The following CSS classes are added and removed on the associated input/select/textarea element - * depending on the validity of the model. - * - * - `ng-valid`: the model is valid - * - `ng-invalid`: the model is invalid - * - `ng-valid-[key]`: for each valid key added by `$setValidity` - * - `ng-invalid-[key]`: for each invalid key added by `$setValidity` - * - `ng-pristine`: the control hasn't been interacted with yet - * - `ng-dirty`: the control has been interacted with - * - `ng-touched`: the control has been blurred - * - `ng-untouched`: the control hasn't been blurred - * - `ng-pending`: any `$asyncValidators` are unfulfilled - * - * Keep in mind that ngAnimate can detect each of these classes when added and removed. - * - * ## Animation Hooks - * - * Animations within models are triggered when any of the associated CSS classes are added and removed - * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`, - * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. - * The animations that are triggered within ngModel are similar to how they work in ngClass and - * animations can be hooked into using CSS transitions, keyframes as well as JS animations. - * - * The following example shows a simple way to utilize CSS transitions to style an input element - * that has been rendered as invalid after it has been validated: - * - * <pre> - * //be sure to include ngAnimate as a module to hook into more - * //advanced animations - * .my-input { - * transition:0.5s linear all; - * background: white; - * } - * .my-input.ng-invalid { - * background: red; - * color:white; - * } - * </pre> - * - * @example - * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample"> - <file name="index.html"> - <script> - angular.module('inputExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.val = '1'; - }]); - </script> - <style> - .my-input { - transition:all linear 0.5s; - background: transparent; - } - .my-input.ng-invalid { - color:white; - background: red; - } - </style> - <p id="inputDescription"> - Update input to see transitions when valid/invalid. - Integer is a valid value. - </p> - <form name="testForm" ng-controller="ExampleController"> - <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" - aria-describedby="inputDescription" /> - </form> - </file> - * </example> - * - * ## Binding to a getter/setter - * - * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a - * function that returns a representation of the model when called with zero arguments, and sets - * the internal state of a model when called with an argument. It's sometimes useful to use this - * for models that have an internal representation that's different from what the model exposes - * to the view. - * - * <div class="alert alert-success"> - * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more - * frequently than other parts of your code. - * </div> - * - * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that - * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to - * a `<form>`, which will enable this behavior for all `<input>`s within it. See - * {@link ng.directive:ngModelOptions `ngModelOptions`} for more. - * - * The following example shows how to use `ngModel` with a getter/setter: - * - * @example - * <example name="ngModel-getter-setter" module="getterSetterExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <form name="userForm"> - <label>Name: - <input type="text" name="userName" - ng-model="user.name" - ng-model-options="{ getterSetter: true }" /> - </label> - </form> - <pre>user.name = <span ng-bind="user.name()"></span></pre> - </div> - </file> - <file name="app.js"> - angular.module('getterSetterExample', []) - .controller('ExampleController', ['$scope', function($scope) { - var _name = 'Brian'; - $scope.user = { - name: function(newName) { - // Note that newName can be undefined for two reasons: - // 1. Because it is called as a getter and thus called with no arguments - // 2. Because the property should actually be set to undefined. This happens e.g. if the - // input is invalid - return arguments.length ? (_name = newName) : _name; - } - }; - }]); - </file> - * </example> - */ -var ngModelDirective = ['$rootScope', function($rootScope) { - return { - restrict: 'A', - require: ['ngModel', '^?form', '^?ngModelOptions'], - controller: NgModelController, - // Prelink needs to run before any input directive - // so that we can set the NgModelOptions in NgModelController - // before anyone else uses it. - priority: 1, - compile: function ngModelCompile(element) { - // Setup initial state of the control - element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS); - - return { - pre: function ngModelPreLink(scope, element, attr, ctrls) { - var modelCtrl = ctrls[0], - formCtrl = ctrls[1] || modelCtrl.$$parentForm; - - modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options); - - // notify others, especially parent forms - formCtrl.$addControl(modelCtrl); - - attr.$observe('name', function(newValue) { - if (modelCtrl.$name !== newValue) { - modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue); - } - }); - - scope.$on('$destroy', function() { - modelCtrl.$$parentForm.$removeControl(modelCtrl); - }); - }, - post: function ngModelPostLink(scope, element, attr, ctrls) { - var modelCtrl = ctrls[0]; - if (modelCtrl.$options && modelCtrl.$options.updateOn) { - element.on(modelCtrl.$options.updateOn, function(ev) { - modelCtrl.$$debounceViewValueCommit(ev && ev.type); - }); - } - - element.on('blur', function(ev) { - if (modelCtrl.$touched) return; - - if ($rootScope.$$phase) { - scope.$evalAsync(modelCtrl.$setTouched); - } else { - scope.$apply(modelCtrl.$setTouched); - } - }); - } - }; - } - }; -}]; - -var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; - -/** - * @ngdoc directive - * @name ngModelOptions - * - * @description - * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of - * events that will trigger a model update and/or a debouncing delay so that the actual update only - * takes place when a timer expires; this timer will be reset after another change takes place. - * - * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might - * be different from the value in the actual model. This means that if you update the model you - * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in - * order to make sure it is synchronized with the model and that any debounced action is canceled. - * - * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`} - * method is by making sure the input is placed inside a form that has a `name` attribute. This is - * important because `form` controllers are published to the related scope under the name in their - * `name` attribute. - * - * Any pending changes will take place immediately when an enclosing form is submitted via the - * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` - * to have access to the updated model. - * - * `ngModelOptions` has an effect on the element it's declared on and its descendants. - * - * @param {Object} ngModelOptions options to apply to the current model. Valid keys are: - * - `updateOn`: string specifying which event should the input be bound to. You can set several - * events using an space delimited list. There is a special event called `default` that - * matches the default events belonging of the control. - * - `debounce`: integer value which contains the debounce model update value in milliseconds. A - * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a - * custom value for each event. For example: - * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"` - * - `allowInvalid`: boolean value which indicates that the model can be set with values that did - * not validate correctly instead of the default behavior of setting the model to undefined. - * - `getterSetter`: boolean value which determines whether or not to treat functions bound to - `ngModel` as getters/setters. - * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for - * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the - * continental US time zone abbreviations, but for general use, use a time zone offset, for - * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) - * If not specified, the timezone of the browser will be used. - * - * @example - - The following example shows how to override immediate updates. Changes on the inputs within the - form will update the model only when the control loses focus (blur event). If `escape` key is - pressed while the input field is focused, the value is reset to the value in the current model. - - <example name="ngModelOptions-directive-blur" module="optionsExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <form name="userForm"> - <label>Name: - <input type="text" name="userName" - ng-model="user.name" - ng-model-options="{ updateOn: 'blur' }" - ng-keyup="cancel($event)" /> - </label><br /> - <label>Other data: - <input type="text" ng-model="user.data" /> - </label><br /> - </form> - <pre>user.name = <span ng-bind="user.name"></span></pre> - </div> - </file> - <file name="app.js"> - angular.module('optionsExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'say', data: '' }; - - $scope.cancel = function(e) { - if (e.keyCode == 27) { - $scope.userForm.userName.$rollbackViewValue(); - } - }; - }]); - </file> - <file name="protractor.js" type="protractor"> - var model = element(by.binding('user.name')); - var input = element(by.model('user.name')); - var other = element(by.model('user.data')); - - it('should allow custom events', function() { - input.sendKeys(' hello'); - input.click(); - expect(model.getText()).toEqual('say'); - other.click(); - expect(model.getText()).toEqual('say hello'); - }); - - it('should $rollbackViewValue when model changes', function() { - input.sendKeys(' hello'); - expect(input.getAttribute('value')).toEqual('say hello'); - input.sendKeys(protractor.Key.ESCAPE); - expect(input.getAttribute('value')).toEqual('say'); - other.click(); - expect(model.getText()).toEqual('say'); - }); - </file> - </example> - - This one shows how to debounce model changes. Model will be updated only 1 sec after last change. - If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. - - <example name="ngModelOptions-directive-debounce" module="optionsExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <form name="userForm"> - <label>Name: - <input type="text" name="userName" - ng-model="user.name" - ng-model-options="{ debounce: 1000 }" /> - </label> - <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button> - <br /> - </form> - <pre>user.name = <span ng-bind="user.name"></span></pre> - </div> - </file> - <file name="app.js"> - angular.module('optionsExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.user = { name: 'say' }; - }]); - </file> - </example> - - This one shows how to bind to getter/setters: - - <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <form name="userForm"> - <label>Name: - <input type="text" name="userName" - ng-model="user.name" - ng-model-options="{ getterSetter: true }" /> - </label> - </form> - <pre>user.name = <span ng-bind="user.name()"></span></pre> - </div> - </file> - <file name="app.js"> - angular.module('getterSetterExample', []) - .controller('ExampleController', ['$scope', function($scope) { - var _name = 'Brian'; - $scope.user = { - name: function(newName) { - // Note that newName can be undefined for two reasons: - // 1. Because it is called as a getter and thus called with no arguments - // 2. Because the property should actually be set to undefined. This happens e.g. if the - // input is invalid - return arguments.length ? (_name = newName) : _name; - } - }; - }]); - </file> - </example> - */ -var ngModelOptionsDirective = function() { - return { - restrict: 'A', - controller: ['$scope', '$attrs', function($scope, $attrs) { - var that = this; - this.$options = copy($scope.$eval($attrs.ngModelOptions)); - // Allow adding/overriding bound events - if (isDefined(this.$options.updateOn)) { - this.$options.updateOnDefault = false; - // extract "default" pseudo-event from list of events that can trigger a model update - this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() { - that.$options.updateOnDefault = true; - return ' '; - })); - } else { - this.$options.updateOnDefault = true; - } - }] - }; -}; - - - -// helper methods -function addSetValidityMethod(context) { - var ctrl = context.ctrl, - $element = context.$element, - classCache = {}, - set = context.set, - unset = context.unset, - $animate = context.$animate; - - classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS)); - - ctrl.$setValidity = setValidity; - - function setValidity(validationErrorKey, state, controller) { - if (isUndefined(state)) { - createAndSet('$pending', validationErrorKey, controller); - } else { - unsetAndCleanup('$pending', validationErrorKey, controller); - } - if (!isBoolean(state)) { - unset(ctrl.$error, validationErrorKey, controller); - unset(ctrl.$$success, validationErrorKey, controller); - } else { - if (state) { - unset(ctrl.$error, validationErrorKey, controller); - set(ctrl.$$success, validationErrorKey, controller); - } else { - set(ctrl.$error, validationErrorKey, controller); - unset(ctrl.$$success, validationErrorKey, controller); - } - } - if (ctrl.$pending) { - cachedToggleClass(PENDING_CLASS, true); - ctrl.$valid = ctrl.$invalid = undefined; - toggleValidationCss('', null); - } else { - cachedToggleClass(PENDING_CLASS, false); - ctrl.$valid = isObjectEmpty(ctrl.$error); - ctrl.$invalid = !ctrl.$valid; - toggleValidationCss('', ctrl.$valid); - } - - // re-read the state as the set/unset methods could have - // combined state in ctrl.$error[validationError] (used for forms), - // where setting/unsetting only increments/decrements the value, - // and does not replace it. - var combinedState; - if (ctrl.$pending && ctrl.$pending[validationErrorKey]) { - combinedState = undefined; - } else if (ctrl.$error[validationErrorKey]) { - combinedState = false; - } else if (ctrl.$$success[validationErrorKey]) { - combinedState = true; - } else { - combinedState = null; - } - - toggleValidationCss(validationErrorKey, combinedState); - ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl); - } - - function createAndSet(name, value, controller) { - if (!ctrl[name]) { - ctrl[name] = {}; - } - set(ctrl[name], value, controller); - } - - function unsetAndCleanup(name, value, controller) { - if (ctrl[name]) { - unset(ctrl[name], value, controller); - } - if (isObjectEmpty(ctrl[name])) { - ctrl[name] = undefined; - } - } - - function cachedToggleClass(className, switchValue) { - if (switchValue && !classCache[className]) { - $animate.addClass($element, className); - classCache[className] = true; - } else if (!switchValue && classCache[className]) { - $animate.removeClass($element, className); - classCache[className] = false; - } - } - - function toggleValidationCss(validationErrorKey, isValid) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - - cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true); - cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false); - } -} - -function isObjectEmpty(obj) { - if (obj) { - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - return false; - } - } - } - return true; -} - -/** - * @ngdoc directive - * @name ngNonBindable - * @restrict AC - * @priority 1000 - * - * @description - * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current - * DOM element. This is useful if the element contains what appears to be Angular directives and - * bindings but which should be ignored by Angular. This could be the case if you have a site that - * displays snippets of code, for instance. - * - * @element ANY - * - * @example - * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, - * but the one wrapped in `ngNonBindable` is left alone. - * - * @example - <example> - <file name="index.html"> - <div>Normal: {{1 + 2}}</div> - <div ng-non-bindable>Ignored: {{1 + 2}}</div> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-non-bindable', function() { - expect(element(by.binding('1 + 2')).getText()).toContain('3'); - expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); - }); - </file> - </example> - */ -var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); - -/* global jqLiteRemove */ - -var ngOptionsMinErr = minErr('ngOptions'); - -/** - * @ngdoc directive - * @name ngOptions - * @restrict A - * - * @description - * - * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` - * elements for the `<select>` element using the array or object obtained by evaluating the - * `ngOptions` comprehension expression. - * - * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a - * similar result. However, `ngOptions` provides some benefits such as reducing memory and - * increasing speed by not creating a new scope for each repeated instance, as well as providing - * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the - * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound - * to a non-string value. This is because an option element can only be bound to string values at - * present. - * - * When an item in the `<select>` menu is selected, the array element or object property - * represented by the selected option will be bound to the model identified by the `ngModel` - * directive. - * - * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can - * be nested into the `<select>` element. This element will then represent the `null` or "not selected" - * option. See example below for demonstration. - * - * ## Complex Models (objects or collections) - * - * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when - * binding any input directive to a model that is an object or a collection. - * - * Since this is a common situation for `ngOptions` the directive additionally watches the model using - * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in - * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual - * object/collection has not changed identity but only a property on the object or an item in the collection - * changes. - * - * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection - * if the model is an array). This means that changing a property deeper inside the object/collection that the - * first level will not trigger a re-rendering. - * - * - * ## `select` **`as`** - * - * Using `select` **`as`** will bind the result of the `select` expression to the model, but - * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) - * or property name (for object data sources) of the value within the collection. If a **`track by`** expression - * is used, the result of that expression will be set as the value of the `option` and `select` elements. - * - * - * ### `select` **`as`** and **`track by`** - * - * <div class="alert alert-warning"> - * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together. - * </div> - * - * Consider the following example: - * - * ```html - * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select> - * ``` - * - * ```js - * $scope.values = [{ - * id: 1, - * label: 'aLabel', - * subItem: { name: 'aSubItem' } - * }, { - * id: 2, - * label: 'bLabel', - * subItem: { name: 'bSubItem' } - * }]; - * - * $scope.selected = { name: 'aSubItem' }; - * ``` - * - * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element - * of the data source (to `item` in this example). To calculate whether an element is selected, we do the - * following: - * - * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]` - * 2. Apply **`track by`** to the already selected value in `ngModel`. - * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected - * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to - * a wrong object, the selected element can't be found, `<select>` is always reset to the "not - * selected" option. - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required The control is considered valid only if value is entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {comprehension_expression=} ngOptions in one of the following forms: - * - * * for array data sources: - * * `label` **`for`** `value` **`in`** `array` - * * `select` **`as`** `label` **`for`** `value` **`in`** `array` - * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` - * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` - * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` - * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` - * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr` - * (for including a filter with `track by`) - * * for object data sources: - * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` - * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`group by`** `group` - * **`for` `(`**`key`**`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`disable when`** `disable` - * **`for` `(`**`key`**`,`** `value`**`) in`** `object` - * - * Where: - * - * * `array` / `object`: an expression which evaluates to an array / object to iterate over. - * * `value`: local variable which will refer to each item in the `array` or each property value - * of `object` during iteration. - * * `key`: local variable which will refer to a property name in `object` during iteration. - * * `label`: The result of this expression will be the label for `<option>` element. The - * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). - * * `select`: The result of this expression will be bound to the model of the parent `<select>` - * element. If not specified, `select` expression will default to `value`. - * * `group`: The result of this expression will be used to group options using the `<optgroup>` - * DOM element. - * * `disable`: The result of this expression will be used to disable the rendered `<option>` - * element. Return `true` to disable. - * * `trackexpr`: Used when working with an array of objects. The result of this expression will be - * used to identify the objects in the array. The `trackexpr` will most likely refer to the - * `value` variable (e.g. `value.propertyName`). With this the selection is preserved - * even when the options are recreated (e.g. reloaded from the server). - * - * @example - <example module="selectExample"> - <file name="index.html"> - <script> - angular.module('selectExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.colors = [ - {name:'black', shade:'dark'}, - {name:'white', shade:'light', notAnOption: true}, - {name:'red', shade:'dark'}, - {name:'blue', shade:'dark', notAnOption: true}, - {name:'yellow', shade:'light', notAnOption: false} - ]; - $scope.myColor = $scope.colors[2]; // red - }]); - </script> - <div ng-controller="ExampleController"> - <ul> - <li ng-repeat="color in colors"> - <label>Name: <input ng-model="color.name"></label> - <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label> - <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button> - </li> - <li> - <button ng-click="colors.push({})">add</button> - </li> - </ul> - <hr/> - <label>Color (null not allowed): - <select ng-model="myColor" ng-options="color.name for color in colors"></select> - </label><br/> - <label>Color (null allowed): - <span class="nullable"> - <select ng-model="myColor" ng-options="color.name for color in colors"> - <option value="">-- choose color --</option> - </select> - </span></label><br/> - - <label>Color grouped by shade: - <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors"> - </select> - </label><br/> - - <label>Color grouped by shade, with some disabled: - <select ng-model="myColor" - ng-options="color.name group by color.shade disable when color.notAnOption for color in colors"> - </select> - </label><br/> - - - - Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>. - <br/> - <hr/> - Currently selected: {{ {selected_color:myColor} }} - <div style="border:solid 1px black; height:20px" - ng-style="{'background-color':myColor.name}"> - </div> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should check ng-options', function() { - expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); - element.all(by.model('myColor')).first().click(); - element.all(by.css('select[ng-model="myColor"] option')).first().click(); - expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); - element(by.css('.nullable select[ng-model="myColor"]')).click(); - element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); - expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); - }); - </file> - </example> - */ - -// jshint maxlen: false -// //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999 -var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/; - // 1: value expression (valueFn) - // 2: label expression (displayFn) - // 3: group by expression (groupByFn) - // 4: disable when expression (disableWhenFn) - // 5: array item variable name - // 6: object item key variable name - // 7: object item value variable name - // 8: collection expression - // 9: track by expression -// jshint maxlen: 100 - - -var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { - - function parseOptionsExpression(optionsExp, selectElement, scope) { - - var match = optionsExp.match(NG_OPTIONS_REGEXP); - if (!(match)) { - throw ngOptionsMinErr('iexp', - "Expected expression in form of " + - "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + - " but got '{0}'. Element: {1}", - optionsExp, startingTag(selectElement)); - } - - // Extract the parts from the ngOptions expression - - // The variable name for the value of the item in the collection - var valueName = match[5] || match[7]; - // The variable name for the key of the item in the collection - var keyName = match[6]; - - // An expression that generates the viewValue for an option if there is a label expression - var selectAs = / as /.test(match[0]) && match[1]; - // An expression that is used to track the id of each object in the options collection - var trackBy = match[9]; - // An expression that generates the viewValue for an option if there is no label expression - var valueFn = $parse(match[2] ? match[1] : valueName); - var selectAsFn = selectAs && $parse(selectAs); - var viewValueFn = selectAsFn || valueFn; - var trackByFn = trackBy && $parse(trackBy); - - // Get the value by which we are going to track the option - // if we have a trackFn then use that (passing scope and locals) - // otherwise just hash the given viewValue - var getTrackByValueFn = trackBy ? - function(value, locals) { return trackByFn(scope, locals); } : - function getHashOfValue(value) { return hashKey(value); }; - var getTrackByValue = function(value, key) { - return getTrackByValueFn(value, getLocals(value, key)); - }; - - var displayFn = $parse(match[2] || match[1]); - var groupByFn = $parse(match[3] || ''); - var disableWhenFn = $parse(match[4] || ''); - var valuesFn = $parse(match[8]); - - var locals = {}; - var getLocals = keyName ? function(value, key) { - locals[keyName] = key; - locals[valueName] = value; - return locals; - } : function(value) { - locals[valueName] = value; - return locals; - }; - - - function Option(selectValue, viewValue, label, group, disabled) { - this.selectValue = selectValue; - this.viewValue = viewValue; - this.label = label; - this.group = group; - this.disabled = disabled; - } - - function getOptionValuesKeys(optionValues) { - var optionValuesKeys; - - if (!keyName && isArrayLike(optionValues)) { - optionValuesKeys = optionValues; - } else { - // if object, extract keys, in enumeration order, unsorted - optionValuesKeys = []; - for (var itemKey in optionValues) { - if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') { - optionValuesKeys.push(itemKey); - } - } - } - return optionValuesKeys; - } - - return { - trackBy: trackBy, - getTrackByValue: getTrackByValue, - getWatchables: $parse(valuesFn, function(optionValues) { - // Create a collection of things that we would like to watch (watchedArray) - // so that they can all be watched using a single $watchCollection - // that only runs the handler once if anything changes - var watchedArray = []; - optionValues = optionValues || []; - - var optionValuesKeys = getOptionValuesKeys(optionValues); - var optionValuesLength = optionValuesKeys.length; - for (var index = 0; index < optionValuesLength; index++) { - var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index]; - var value = optionValues[key]; - - var locals = getLocals(optionValues[key], key); - var selectValue = getTrackByValueFn(optionValues[key], locals); - watchedArray.push(selectValue); - - // Only need to watch the displayFn if there is a specific label expression - if (match[2] || match[1]) { - var label = displayFn(scope, locals); - watchedArray.push(label); - } - - // Only need to watch the disableWhenFn if there is a specific disable expression - if (match[4]) { - var disableWhen = disableWhenFn(scope, locals); - watchedArray.push(disableWhen); - } - } - return watchedArray; - }), - - getOptions: function() { - - var optionItems = []; - var selectValueMap = {}; - - // The option values were already computed in the `getWatchables` fn, - // which must have been called to trigger `getOptions` - var optionValues = valuesFn(scope) || []; - var optionValuesKeys = getOptionValuesKeys(optionValues); - var optionValuesLength = optionValuesKeys.length; - - for (var index = 0; index < optionValuesLength; index++) { - var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index]; - var value = optionValues[key]; - var locals = getLocals(value, key); - var viewValue = viewValueFn(scope, locals); - var selectValue = getTrackByValueFn(viewValue, locals); - var label = displayFn(scope, locals); - var group = groupByFn(scope, locals); - var disabled = disableWhenFn(scope, locals); - var optionItem = new Option(selectValue, viewValue, label, group, disabled); - - optionItems.push(optionItem); - selectValueMap[selectValue] = optionItem; - } - - return { - items: optionItems, - selectValueMap: selectValueMap, - getOptionFromViewValue: function(value) { - return selectValueMap[getTrackByValue(value)]; - }, - getViewValueFromOption: function(option) { - // If the viewValue could be an object that may be mutated by the application, - // we need to make a copy and not return the reference to the value on the option. - return trackBy ? angular.copy(option.viewValue) : option.viewValue; - } - }; - } - }; - } - - - // we can't just jqLite('<option>') since jqLite is not smart enough - // to create it in <select> and IE barfs otherwise. - var optionTemplate = document.createElement('option'), - optGroupTemplate = document.createElement('optgroup'); - - return { - restrict: 'A', - terminal: true, - require: ['select', '?ngModel'], - link: function(scope, selectElement, attr, ctrls) { - - // if ngModel is not defined, we don't need to do anything - var ngModelCtrl = ctrls[1]; - if (!ngModelCtrl) return; - - var selectCtrl = ctrls[0]; - var multiple = attr.multiple; - - // The emptyOption allows the application developer to provide their own custom "empty" - // option when the viewValue does not match any of the option values. - var emptyOption; - for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) { - if (children[i].value === '') { - emptyOption = children.eq(i); - break; - } - } - - var providedEmptyOption = !!emptyOption; - - var unknownOption = jqLite(optionTemplate.cloneNode(false)); - unknownOption.val('?'); - - var options; - var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope); - - - var renderEmptyOption = function() { - if (!providedEmptyOption) { - selectElement.prepend(emptyOption); - } - selectElement.val(''); - emptyOption.prop('selected', true); // needed for IE - emptyOption.attr('selected', true); - }; - - var removeEmptyOption = function() { - if (!providedEmptyOption) { - emptyOption.remove(); - } - }; - - - var renderUnknownOption = function() { - selectElement.prepend(unknownOption); - selectElement.val('?'); - unknownOption.prop('selected', true); // needed for IE - unknownOption.attr('selected', true); - }; - - var removeUnknownOption = function() { - unknownOption.remove(); - }; - - - // Update the controller methods for multiple selectable options - if (!multiple) { - - selectCtrl.writeValue = function writeNgOptionsValue(value) { - var option = options.getOptionFromViewValue(value); - - if (option && !option.disabled) { - if (selectElement[0].value !== option.selectValue) { - removeUnknownOption(); - removeEmptyOption(); - - selectElement[0].value = option.selectValue; - option.element.selected = true; - option.element.setAttribute('selected', 'selected'); - } - } else { - if (value === null || providedEmptyOption) { - removeUnknownOption(); - renderEmptyOption(); - } else { - removeEmptyOption(); - renderUnknownOption(); - } - } - }; - - selectCtrl.readValue = function readNgOptionsValue() { - - var selectedOption = options.selectValueMap[selectElement.val()]; - - if (selectedOption && !selectedOption.disabled) { - removeEmptyOption(); - removeUnknownOption(); - return options.getViewValueFromOption(selectedOption); - } - return null; - }; - - // If we are using `track by` then we must watch the tracked value on the model - // since ngModel only watches for object identity change - if (ngOptions.trackBy) { - scope.$watch( - function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); }, - function() { ngModelCtrl.$render(); } - ); - } - - } else { - - ngModelCtrl.$isEmpty = function(value) { - return !value || value.length === 0; - }; - - - selectCtrl.writeValue = function writeNgOptionsMultiple(value) { - options.items.forEach(function(option) { - option.element.selected = false; - }); - - if (value) { - value.forEach(function(item) { - var option = options.getOptionFromViewValue(item); - if (option && !option.disabled) option.element.selected = true; - }); - } - }; - - - selectCtrl.readValue = function readNgOptionsMultiple() { - var selectedValues = selectElement.val() || [], - selections = []; - - forEach(selectedValues, function(value) { - var option = options.selectValueMap[value]; - if (option && !option.disabled) selections.push(options.getViewValueFromOption(option)); - }); - - return selections; - }; - - // If we are using `track by` then we must watch these tracked values on the model - // since ngModel only watches for object identity change - if (ngOptions.trackBy) { - - scope.$watchCollection(function() { - if (isArray(ngModelCtrl.$viewValue)) { - return ngModelCtrl.$viewValue.map(function(value) { - return ngOptions.getTrackByValue(value); - }); - } - }, function() { - ngModelCtrl.$render(); - }); - - } - } - - - if (providedEmptyOption) { - - // we need to remove it before calling selectElement.empty() because otherwise IE will - // remove the label from the element. wtf? - emptyOption.remove(); - - // compile the element since there might be bindings in it - $compile(emptyOption)(scope); - - // remove the class, which is added automatically because we recompile the element and it - // becomes the compilation root - emptyOption.removeClass('ng-scope'); - } else { - emptyOption = jqLite(optionTemplate.cloneNode(false)); - } - - // We need to do this here to ensure that the options object is defined - // when we first hit it in writeNgOptionsValue - updateOptions(); - - // We will re-render the option elements if the option values or labels change - scope.$watchCollection(ngOptions.getWatchables, updateOptions); - - // ------------------------------------------------------------------ // - - - function updateOptionElement(option, element) { - option.element = element; - element.disabled = option.disabled; - // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive - // selects in certain circumstances when multiple selects are next to each other and display - // the option list in listbox style, i.e. the select is [multiple], or specifies a [size]. - // See https://github.com/angular/angular.js/issues/11314 for more info. - // This is unfortunately untestable with unit / e2e tests - if (option.label !== element.label) { - element.label = option.label; - element.textContent = option.label; - } - if (option.value !== element.value) element.value = option.selectValue; - } - - function addOrReuseElement(parent, current, type, templateElement) { - var element; - // Check whether we can reuse the next element - if (current && lowercase(current.nodeName) === type) { - // The next element is the right type so reuse it - element = current; - } else { - // The next element is not the right type so create a new one - element = templateElement.cloneNode(false); - if (!current) { - // There are no more elements so just append it to the select - parent.appendChild(element); - } else { - // The next element is not a group so insert the new one - parent.insertBefore(element, current); - } - } - return element; - } - - - function removeExcessElements(current) { - var next; - while (current) { - next = current.nextSibling; - jqLiteRemove(current); - current = next; - } - } - - - function skipEmptyAndUnknownOptions(current) { - var emptyOption_ = emptyOption && emptyOption[0]; - var unknownOption_ = unknownOption && unknownOption[0]; - - if (emptyOption_ || unknownOption_) { - while (current && - (current === emptyOption_ || - current === unknownOption_ || - emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) { - // Empty options might have directives that transclude - // and insert comments (e.g. ngIf) - current = current.nextSibling; - } - } - return current; - } - - - function updateOptions() { - - var previousValue = options && selectCtrl.readValue(); - - options = ngOptions.getOptions(); - - var groupMap = {}; - var currentElement = selectElement[0].firstChild; - - // Ensure that the empty option is always there if it was explicitly provided - if (providedEmptyOption) { - selectElement.prepend(emptyOption); - } - - currentElement = skipEmptyAndUnknownOptions(currentElement); - - options.items.forEach(function updateOption(option) { - var group; - var groupElement; - var optionElement; - - if (option.group) { - - // This option is to live in a group - // See if we have already created this group - group = groupMap[option.group]; - - if (!group) { - - // We have not already created this group - groupElement = addOrReuseElement(selectElement[0], - currentElement, - 'optgroup', - optGroupTemplate); - // Move to the next element - currentElement = groupElement.nextSibling; - - // Update the label on the group element - groupElement.label = option.group; - - // Store it for use later - group = groupMap[option.group] = { - groupElement: groupElement, - currentOptionElement: groupElement.firstChild - }; - - } - - // So now we have a group for this option we add the option to the group - optionElement = addOrReuseElement(group.groupElement, - group.currentOptionElement, - 'option', - optionTemplate); - updateOptionElement(option, optionElement); - // Move to the next element - group.currentOptionElement = optionElement.nextSibling; - - } else { - - // This option is not in a group - optionElement = addOrReuseElement(selectElement[0], - currentElement, - 'option', - optionTemplate); - updateOptionElement(option, optionElement); - // Move to the next element - currentElement = optionElement.nextSibling; - } - }); - - - // Now remove all excess options and group - Object.keys(groupMap).forEach(function(key) { - removeExcessElements(groupMap[key].currentOptionElement); - }); - removeExcessElements(currentElement); - - ngModelCtrl.$render(); - - // Check to see if the value has changed due to the update to the options - if (!ngModelCtrl.$isEmpty(previousValue)) { - var nextValue = selectCtrl.readValue(); - if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) { - ngModelCtrl.$setViewValue(nextValue); - ngModelCtrl.$render(); - } - } - - } - - } - }; -}]; - -/** - * @ngdoc directive - * @name ngPluralize - * @restrict EA - * - * @description - * `ngPluralize` is a directive that displays messages according to en-US localization rules. - * These rules are bundled with angular.js, but can be overridden - * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive - * by specifying the mappings between - * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) - * and the strings to be displayed. - * - * # Plural categories and explicit number rules - * There are two - * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) - * in Angular's default en-US locale: "one" and "other". - * - * While a plural category may match many numbers (for example, in en-US locale, "other" can match - * any number that is not 1), an explicit number rule can only match one number. For example, the - * explicit number rule for "3" matches the number 3. There are examples of plural categories - * and explicit number rules throughout the rest of this documentation. - * - * # Configuring ngPluralize - * You configure ngPluralize by providing 2 attributes: `count` and `when`. - * You can also provide an optional attribute, `offset`. - * - * The value of the `count` attribute can be either a string or an {@link guide/expression - * Angular expression}; these are evaluated on the current scope for its bound value. - * - * The `when` attribute specifies the mappings between plural categories and the actual - * string to be displayed. The value of the attribute should be a JSON object. - * - * The following example shows how to configure ngPluralize: - * - * ```html - * <ng-pluralize count="personCount" - when="{'0': 'Nobody is viewing.', - * 'one': '1 person is viewing.', - * 'other': '{} people are viewing.'}"> - * </ng-pluralize> - *``` - * - * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not - * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" - * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for - * other numbers, for example 12, so that instead of showing "12 people are viewing", you can - * show "a dozen people are viewing". - * - * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted - * into pluralized strings. In the previous example, Angular will replace `{}` with - * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder - * for <span ng-non-bindable>{{numberExpression}}</span>. - * - * If no rule is defined for a category, then an empty string is displayed and a warning is generated. - * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`. - * - * # Configuring ngPluralize with offset - * The `offset` attribute allows further customization of pluralized text, which can result in - * a better user experience. For example, instead of the message "4 people are viewing this document", - * you might display "John, Kate and 2 others are viewing this document". - * The offset attribute allows you to offset a number by any desired value. - * Let's take a look at an example: - * - * ```html - * <ng-pluralize count="personCount" offset=2 - * when="{'0': 'Nobody is viewing.', - * '1': '{{person1}} is viewing.', - * '2': '{{person1}} and {{person2}} are viewing.', - * 'one': '{{person1}}, {{person2}} and one other person are viewing.', - * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> - * </ng-pluralize> - * ``` - * - * Notice that we are still using two plural categories(one, other), but we added - * three explicit number rules 0, 1 and 2. - * When one person, perhaps John, views the document, "John is viewing" will be shown. - * When three people view the document, no explicit number rule is found, so - * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. - * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" - * is shown. - * - * Note that when you specify offsets, you must provide explicit number rules for - * numbers from 0 up to and including the offset. If you use an offset of 3, for example, - * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for - * plural categories "one" and "other". - * - * @param {string|expression} count The variable to be bound to. - * @param {string} when The mapping between plural category to its corresponding strings. - * @param {number=} offset Offset to deduct from the total number. - * - * @example - <example module="pluralizeExample"> - <file name="index.html"> - <script> - angular.module('pluralizeExample', []) - .controller('ExampleController', ['$scope', function($scope) { - $scope.person1 = 'Igor'; - $scope.person2 = 'Misko'; - $scope.personCount = 1; - }]); - </script> - <div ng-controller="ExampleController"> - <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/> - <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/> - <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/> - - <!--- Example with simple pluralization rules for en locale ---> - Without Offset: - <ng-pluralize count="personCount" - when="{'0': 'Nobody is viewing.', - 'one': '1 person is viewing.', - 'other': '{} people are viewing.'}"> - </ng-pluralize><br> - - <!--- Example with offset ---> - With Offset(2): - <ng-pluralize count="personCount" offset=2 - when="{'0': 'Nobody is viewing.', - '1': '{{person1}} is viewing.', - '2': '{{person1}} and {{person2}} are viewing.', - 'one': '{{person1}}, {{person2}} and one other person are viewing.', - 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> - </ng-pluralize> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should show correct pluralized string', function() { - var withoutOffset = element.all(by.css('ng-pluralize')).get(0); - var withOffset = element.all(by.css('ng-pluralize')).get(1); - var countInput = element(by.model('personCount')); - - expect(withoutOffset.getText()).toEqual('1 person is viewing.'); - expect(withOffset.getText()).toEqual('Igor is viewing.'); - - countInput.clear(); - countInput.sendKeys('0'); - - expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); - expect(withOffset.getText()).toEqual('Nobody is viewing.'); - - countInput.clear(); - countInput.sendKeys('2'); - - expect(withoutOffset.getText()).toEqual('2 people are viewing.'); - expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); - - countInput.clear(); - countInput.sendKeys('3'); - - expect(withoutOffset.getText()).toEqual('3 people are viewing.'); - expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); - - countInput.clear(); - countInput.sendKeys('4'); - - expect(withoutOffset.getText()).toEqual('4 people are viewing.'); - expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); - }); - it('should show data-bound names', function() { - var withOffset = element.all(by.css('ng-pluralize')).get(1); - var personCount = element(by.model('personCount')); - var person1 = element(by.model('person1')); - var person2 = element(by.model('person2')); - personCount.clear(); - personCount.sendKeys('4'); - person1.clear(); - person1.sendKeys('Di'); - person2.clear(); - person2.sendKeys('Vojta'); - expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); - }); - </file> - </example> - */ -var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) { - var BRACE = /{}/g, - IS_WHEN = /^when(Minus)?(.+)$/; - - return { - link: function(scope, element, attr) { - var numberExp = attr.count, - whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs - offset = attr.offset || 0, - whens = scope.$eval(whenExp) || {}, - whensExpFns = {}, - startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol, - watchRemover = angular.noop, - lastCount; - - forEach(attr, function(expression, attributeName) { - var tmpMatch = IS_WHEN.exec(attributeName); - if (tmpMatch) { - var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]); - whens[whenKey] = element.attr(attr.$attr[attributeName]); - } - }); - forEach(whens, function(expression, key) { - whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement)); - - }); - - scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { - var count = parseFloat(newVal); - var countIsNaN = isNaN(count); - - if (!countIsNaN && !(count in whens)) { - // If an explicit number rule such as 1, 2, 3... is defined, just use it. - // Otherwise, check it against pluralization rules in $locale service. - count = $locale.pluralCat(count - offset); - } - - // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. - // In JS `NaN !== NaN`, so we have to exlicitly check. - if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) { - watchRemover(); - var whenExpFn = whensExpFns[count]; - if (isUndefined(whenExpFn)) { - if (newVal != null) { - $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp); - } - watchRemover = noop; - updateElementText(); - } else { - watchRemover = scope.$watch(whenExpFn, updateElementText); - } - lastCount = count; - } - }); - - function updateElementText(newText) { - element.text(newText || ''); - } - } - }; -}]; - -/** - * @ngdoc directive - * @name ngRepeat - * @multiElement - * - * @description - * The `ngRepeat` directive instantiates a template once per item from a collection. Each template - * instance gets its own scope, where the given loop variable is set to the current collection item, - * and `$index` is set to the item index or key. - * - * Special properties are exposed on the local scope of each template instance, including: - * - * | Variable | Type | Details | - * |-----------|-----------------|-----------------------------------------------------------------------------| - * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | - * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | - * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | - * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | - * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | - * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | - * - * <div class="alert alert-info"> - * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. - * This may be useful when, for instance, nesting ngRepeats. - * </div> - * - * - * # Iterating over object properties - * - * It is possible to get `ngRepeat` to iterate over the properties of an object using the following - * syntax: - * - * ```js - * <div ng-repeat="(key, value) in myObj"> ... </div> - * ``` - * - * You need to be aware that the JavaScript specification does not define the order of keys - * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive - * used to sort the keys alphabetically.) - * - * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser - * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing - * keys in the order in which they were defined, although there are exceptions when keys are deleted - * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues - * - * If this is not desired, the recommended workaround is to convert your object into an array - * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could - * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter) - * or implement a `$watch` on the object yourself. - * - * - * # Tracking and Duplicates - * - * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM: - * - * * When an item is added, a new instance of the template is added to the DOM. - * * When an item is removed, its template instance is removed from the DOM. - * * When items are reordered, their respective templates are reordered in the DOM. - * - * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when - * there are duplicates, it is not possible to maintain a one-to-one mapping between collection - * items and DOM elements. - * - * If you do need to repeat duplicate items, you can substitute the default tracking behavior - * with your own using the `track by` expression. - * - * For example, you may track items by the index of each item in the collection, using the - * special scope property `$index`: - * ```html - * <div ng-repeat="n in [42, 42, 43, 43] track by $index"> - * {{n}} - * </div> - * ``` - * - * You may use arbitrary expressions in `track by`, including references to custom functions - * on the scope: - * ```html - * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)"> - * {{n}} - * </div> - * ``` - * - * If you are working with objects that have an identifier property, you can track - * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat` - * will not have to rebuild the DOM elements for items it has already rendered, even if the - * JavaScript objects in the collection have been substituted for new ones: - * ```html - * <div ng-repeat="model in collection track by model.id"> - * {{model.name}} - * </div> - * ``` - * - * When no `track by` expression is provided, it is equivalent to tracking by the built-in - * `$id` function, which tracks items by their identity: - * ```html - * <div ng-repeat="obj in collection track by $id(obj)"> - * {{obj.prop}} - * </div> - * ``` - * - * <div class="alert alert-warning"> - * **Note:** `track by` must always be the last expression: - * </div> - * ``` - * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id"> - * {{model.name}} - * </div> - * ``` - * - * # Special repeat start and end points - * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending - * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. - * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) - * up to and including the ending HTML tag where **ng-repeat-end** is placed. - * - * The example below makes use of this feature: - * ```html - * <header ng-repeat-start="item in items"> - * Header {{ item }} - * </header> - * <div class="body"> - * Body {{ item }} - * </div> - * <footer ng-repeat-end> - * Footer {{ item }} - * </footer> - * ``` - * - * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: - * ```html - * <header> - * Header A - * </header> - * <div class="body"> - * Body A - * </div> - * <footer> - * Footer A - * </footer> - * <header> - * Header B - * </header> - * <div class="body"> - * Body B - * </div> - * <footer> - * Footer B - * </footer> - * ``` - * - * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such - * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). - * - * @animations - * **.enter** - when a new item is added to the list or when an item is revealed after a filter - * - * **.leave** - when an item is removed from the list or when an item is filtered out - * - * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered - * - * @element ANY - * @scope - * @priority 1000 - * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These - * formats are currently supported: - * - * * `variable in expression` – where variable is the user defined loop variable and `expression` - * is a scope expression giving the collection to enumerate. - * - * For example: `album in artist.albums`. - * - * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, - * and `expression` is the scope expression giving the collection to enumerate. - * - * For example: `(name, age) in {'adam':10, 'amalie':12}`. - * - * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression - * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression - * is specified, ng-repeat associates elements by identity. It is an error to have - * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are - * mapped to the same DOM element, which is not possible.) - * - * Note that the tracking expression must come last, after any filters, and the alias expression. - * - * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements - * will be associated by item identity in the array. - * - * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique - * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements - * with the corresponding item in the array by identity. Moving the same object in array would move the DOM - * element in the same way in the DOM. - * - * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this - * case the object identity does not matter. Two objects are considered equivalent as long as their `id` - * property is same. - * - * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter - * to items in conjunction with a tracking expression. - * - * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the - * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message - * when a filter is active on the repeater, but the filtered result set is empty. - * - * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after - * the items have been processed through the filter. - * - * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end - * (and not as operator, inside an expression). - * - * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` . - * - * @example - * This example initializes the scope to a list of names and - * then uses `ngRepeat` to display every person: - <example module="ngAnimate" deps="angular-animate.js" animations="true"> - <file name="index.html"> - <div ng-init="friends = [ - {name:'John', age:25, gender:'boy'}, - {name:'Jessie', age:30, gender:'girl'}, - {name:'Johanna', age:28, gender:'girl'}, - {name:'Joy', age:15, gender:'girl'}, - {name:'Mary', age:28, gender:'girl'}, - {name:'Peter', age:95, gender:'boy'}, - {name:'Sebastian', age:50, gender:'boy'}, - {name:'Erika', age:27, gender:'girl'}, - {name:'Patrick', age:40, gender:'boy'}, - {name:'Samantha', age:60, gender:'girl'} - ]"> - I have {{friends.length}} friends. They are: - <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" /> - <ul class="example-animate-container"> - <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results"> - [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. - </li> - <li class="animate-repeat" ng-if="results.length == 0"> - <strong>No results found...</strong> - </li> - </ul> - </div> - </file> - <file name="animations.css"> - .example-animate-container { - background:white; - border:1px solid black; - list-style:none; - margin:0; - padding:0 10px; - } - - .animate-repeat { - line-height:40px; - list-style:none; - box-sizing:border-box; - } - - .animate-repeat.ng-move, - .animate-repeat.ng-enter, - .animate-repeat.ng-leave { - transition:all linear 0.5s; - } - - .animate-repeat.ng-leave.ng-leave-active, - .animate-repeat.ng-move, - .animate-repeat.ng-enter { - opacity:0; - max-height:0; - } - - .animate-repeat.ng-leave, - .animate-repeat.ng-move.ng-move-active, - .animate-repeat.ng-enter.ng-enter-active { - opacity:1; - max-height:40px; - } - </file> - <file name="protractor.js" type="protractor"> - var friends = element.all(by.repeater('friend in friends')); - - it('should render initial data set', function() { - expect(friends.count()).toBe(10); - expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); - expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); - expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); - expect(element(by.binding('friends.length')).getText()) - .toMatch("I have 10 friends. They are:"); - }); - - it('should update repeater when filter predicate changes', function() { - expect(friends.count()).toBe(10); - - element(by.model('q')).sendKeys('ma'); - - expect(friends.count()).toBe(2); - expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); - expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); - }); - </file> - </example> - */ -var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { - var NG_REMOVED = '$$NG_REMOVED'; - var ngRepeatMinErr = minErr('ngRepeat'); - - var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) { - // TODO(perf): generate setters to shave off ~40ms or 1-1.5% - scope[valueIdentifier] = value; - if (keyIdentifier) scope[keyIdentifier] = key; - scope.$index = index; - scope.$first = (index === 0); - scope.$last = (index === (arrayLength - 1)); - scope.$middle = !(scope.$first || scope.$last); - // jshint bitwise: false - scope.$odd = !(scope.$even = (index&1) === 0); - // jshint bitwise: true - }; - - var getBlockStart = function(block) { - return block.clone[0]; - }; - - var getBlockEnd = function(block) { - return block.clone[block.clone.length - 1]; - }; - - - return { - restrict: 'A', - multiElement: true, - transclude: 'element', - priority: 1000, - terminal: true, - $$tlb: true, - compile: function ngRepeatCompile($element, $attr) { - var expression = $attr.ngRepeat; - var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' '); - - var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); - - if (!match) { - throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", - expression); - } - - var lhs = match[1]; - var rhs = match[2]; - var aliasAs = match[3]; - var trackByExp = match[4]; - - match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/); - - if (!match) { - throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", - lhs); - } - var valueIdentifier = match[3] || match[1]; - var keyIdentifier = match[2]; - - if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) || - /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) { - throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.", - aliasAs); - } - - var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn; - var hashFnLocals = {$id: hashKey}; - - if (trackByExp) { - trackByExpGetter = $parse(trackByExp); - } else { - trackByIdArrayFn = function(key, value) { - return hashKey(value); - }; - trackByIdObjFn = function(key) { - return key; - }; - } - - return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) { - - if (trackByExpGetter) { - trackByIdExpFn = function(key, value, index) { - // assign key, value, and $index to the locals so that they can be used in hash functions - if (keyIdentifier) hashFnLocals[keyIdentifier] = key; - hashFnLocals[valueIdentifier] = value; - hashFnLocals.$index = index; - return trackByExpGetter($scope, hashFnLocals); - }; - } - - // Store a list of elements from previous run. This is a hash where key is the item from the - // iterator, and the value is objects with following properties. - // - scope: bound scope - // - element: previous element. - // - index: position - // - // We are using no-proto object so that we don't need to guard against inherited props via - // hasOwnProperty. - var lastBlockMap = createMap(); - - //watch props - $scope.$watchCollection(rhs, function ngRepeatAction(collection) { - var index, length, - previousNode = $element[0], // node that cloned nodes should be inserted after - // initialized to the comment node anchor - nextNode, - // Same as lastBlockMap but it has the current state. It will become the - // lastBlockMap on the next iteration. - nextBlockMap = createMap(), - collectionLength, - key, value, // key/value of iteration - trackById, - trackByIdFn, - collectionKeys, - block, // last object information {scope, element, id} - nextBlockOrder, - elementsToRemove; - - if (aliasAs) { - $scope[aliasAs] = collection; - } - - if (isArrayLike(collection)) { - collectionKeys = collection; - trackByIdFn = trackByIdExpFn || trackByIdArrayFn; - } else { - trackByIdFn = trackByIdExpFn || trackByIdObjFn; - // if object, extract keys, in enumeration order, unsorted - collectionKeys = []; - for (var itemKey in collection) { - if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') { - collectionKeys.push(itemKey); - } - } - } - - collectionLength = collectionKeys.length; - nextBlockOrder = new Array(collectionLength); - - // locate existing items - for (index = 0; index < collectionLength; index++) { - key = (collection === collectionKeys) ? index : collectionKeys[index]; - value = collection[key]; - trackById = trackByIdFn(key, value, index); - if (lastBlockMap[trackById]) { - // found previously seen block - block = lastBlockMap[trackById]; - delete lastBlockMap[trackById]; - nextBlockMap[trackById] = block; - nextBlockOrder[index] = block; - } else if (nextBlockMap[trackById]) { - // if collision detected. restore lastBlockMap and throw an error - forEach(nextBlockOrder, function(block) { - if (block && block.scope) lastBlockMap[block.id] = block; - }); - throw ngRepeatMinErr('dupes', - "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", - expression, trackById, value); - } else { - // new never before seen block - nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; - nextBlockMap[trackById] = true; - } - } - - // remove leftover items - for (var blockKey in lastBlockMap) { - block = lastBlockMap[blockKey]; - elementsToRemove = getBlockNodes(block.clone); - $animate.leave(elementsToRemove); - if (elementsToRemove[0].parentNode) { - // if the element was not removed yet because of pending animation, mark it as deleted - // so that we can ignore it later - for (index = 0, length = elementsToRemove.length; index < length; index++) { - elementsToRemove[index][NG_REMOVED] = true; - } - } - block.scope.$destroy(); - } - - // we are not using forEach for perf reasons (trying to avoid #call) - for (index = 0; index < collectionLength; index++) { - key = (collection === collectionKeys) ? index : collectionKeys[index]; - value = collection[key]; - block = nextBlockOrder[index]; - - if (block.scope) { - // if we have already seen this object, then we need to reuse the - // associated scope/element - - nextNode = previousNode; - - // skip nodes that are already pending removal via leave animation - do { - nextNode = nextNode.nextSibling; - } while (nextNode && nextNode[NG_REMOVED]); - - if (getBlockStart(block) != nextNode) { - // existing item which got moved - $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode)); - } - previousNode = getBlockEnd(block); - updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); - } else { - // new item which we don't know about - $transclude(function ngRepeatTransclude(clone, scope) { - block.scope = scope; - // http://jsperf.com/clone-vs-createcomment - var endNode = ngRepeatEndComment.cloneNode(false); - clone[clone.length++] = endNode; - - // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper? - $animate.enter(clone, null, jqLite(previousNode)); - previousNode = endNode; - // Note: We only need the first/last node of the cloned nodes. - // However, we need to keep the reference to the jqlite wrapper as it might be changed later - // by a directive with templateUrl when its template arrives. - block.clone = clone; - nextBlockMap[block.id] = block; - updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); - }); - } - } - lastBlockMap = nextBlockMap; - }); - }; - } - }; -}]; - -var NG_HIDE_CLASS = 'ng-hide'; -var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; -/** - * @ngdoc directive - * @name ngShow - * @multiElement - * - * @description - * The `ngShow` directive shows or hides the given HTML element based on the expression - * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding - * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined - * in AngularJS and sets the display style to none (using an !important flag). - * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). - * - * ```html - * <!-- when $scope.myValue is truthy (element is visible) --> - * <div ng-show="myValue"></div> - * - * <!-- when $scope.myValue is falsy (element is hidden) --> - * <div ng-show="myValue" class="ng-hide"></div> - * ``` - * - * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class - * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed - * from the element causing the element not to appear hidden. - * - * ## Why is !important used? - * - * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector - * can be easily overridden by heavier selectors. For example, something as simple - * as changing the display style on a HTML list item would make hidden elements appear visible. - * This also becomes a bigger issue when dealing with CSS frameworks. - * - * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector - * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the - * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. - * - * ### Overriding `.ng-hide` - * - * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change - * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` - * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope - * with extra animation classes that can be added. - * - * ```css - * .ng-hide:not(.ng-hide-animate) { - * /* this is just another form of hiding an element */ - * display: block!important; - * position: absolute; - * top: -9999px; - * left: -9999px; - * } - * ``` - * - * By default you don't need to override in CSS anything and the animations will work around the display style. - * - * ## A note about animations with `ngShow` - * - * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression - * is true and false. This system works like the animation system present with ngClass except that - * you must also include the !important flag to override the display property - * so that you can perform an animation when the element is hidden during the time of the animation. - * - * ```css - * // - * //a working example can be found at the bottom of this page - * // - * .my-element.ng-hide-add, .my-element.ng-hide-remove { - * /* this is required as of 1.3x to properly - * apply all styling in a show/hide animation */ - * transition: 0s linear all; - * } - * - * .my-element.ng-hide-add-active, - * .my-element.ng-hide-remove-active { - * /* the transition is defined in the active class */ - * transition: 1s linear all; - * } - * - * .my-element.ng-hide-add { ... } - * .my-element.ng-hide-add.ng-hide-add-active { ... } - * .my-element.ng-hide-remove { ... } - * .my-element.ng-hide-remove.ng-hide-remove-active { ... } - * ``` - * - * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display - * property to block during animation states--ngAnimate will handle the style toggling automatically for you. - * - * @animations - * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible - * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden - * - * @element ANY - * @param {expression} ngShow If the {@link guide/expression expression} is truthy - * then the element is shown or hidden respectively. - * - * @example - <example module="ngAnimate" deps="angular-animate.js" animations="true"> - <file name="index.html"> - Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/> - <div> - Show: - <div class="check-element animate-show" ng-show="checked"> - <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. - </div> - </div> - <div> - Hide: - <div class="check-element animate-show" ng-hide="checked"> - <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. - </div> - </div> - </file> - <file name="glyphicons.css"> - @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); - </file> - <file name="animations.css"> - .animate-show { - line-height: 20px; - opacity: 1; - padding: 10px; - border: 1px solid black; - background: white; - } - - .animate-show.ng-hide-add, .animate-show.ng-hide-remove { - transition: all linear 0.5s; - } - - .animate-show.ng-hide { - line-height: 0; - opacity: 0; - padding: 0 10px; - } - - .check-element { - padding: 10px; - border: 1px solid black; - background: white; - } - </file> - <file name="protractor.js" type="protractor"> - var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); - var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); - - it('should check ng-show / ng-hide', function() { - expect(thumbsUp.isDisplayed()).toBeFalsy(); - expect(thumbsDown.isDisplayed()).toBeTruthy(); - - element(by.model('checked')).click(); - - expect(thumbsUp.isDisplayed()).toBeTruthy(); - expect(thumbsDown.isDisplayed()).toBeFalsy(); - }); - </file> - </example> - */ -var ngShowDirective = ['$animate', function($animate) { - return { - restrict: 'A', - multiElement: true, - link: function(scope, element, attr) { - scope.$watch(attr.ngShow, function ngShowWatchAction(value) { - // we're adding a temporary, animation-specific class for ng-hide since this way - // we can control when the element is actually displayed on screen without having - // to have a global/greedy CSS selector that breaks when other animations are run. - // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 - $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, { - tempClasses: NG_HIDE_IN_PROGRESS_CLASS - }); - }); - } - }; -}]; - - -/** - * @ngdoc directive - * @name ngHide - * @multiElement - * - * @description - * The `ngHide` directive shows or hides the given HTML element based on the expression - * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding - * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined - * in AngularJS and sets the display style to none (using an !important flag). - * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). - * - * ```html - * <!-- when $scope.myValue is truthy (element is hidden) --> - * <div ng-hide="myValue" class="ng-hide"></div> - * - * <!-- when $scope.myValue is falsy (element is visible) --> - * <div ng-hide="myValue"></div> - * ``` - * - * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class - * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed - * from the element causing the element not to appear hidden. - * - * ## Why is !important used? - * - * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector - * can be easily overridden by heavier selectors. For example, something as simple - * as changing the display style on a HTML list item would make hidden elements appear visible. - * This also becomes a bigger issue when dealing with CSS frameworks. - * - * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector - * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the - * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. - * - * ### Overriding `.ng-hide` - * - * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change - * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` - * class in CSS: - * - * ```css - * .ng-hide { - * /* this is just another form of hiding an element */ - * display: block!important; - * position: absolute; - * top: -9999px; - * left: -9999px; - * } - * ``` - * - * By default you don't need to override in CSS anything and the animations will work around the display style. - * - * ## A note about animations with `ngHide` - * - * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression - * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` - * CSS class is added and removed for you instead of your own CSS class. - * - * ```css - * // - * //a working example can be found at the bottom of this page - * // - * .my-element.ng-hide-add, .my-element.ng-hide-remove { - * transition: 0.5s linear all; - * } - * - * .my-element.ng-hide-add { ... } - * .my-element.ng-hide-add.ng-hide-add-active { ... } - * .my-element.ng-hide-remove { ... } - * .my-element.ng-hide-remove.ng-hide-remove-active { ... } - * ``` - * - * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display - * property to block during animation states--ngAnimate will handle the style toggling automatically for you. - * - * @animations - * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden - * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible - * - * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} is truthy then - * the element is shown or hidden respectively. - * - * @example - <example module="ngAnimate" deps="angular-animate.js" animations="true"> - <file name="index.html"> - Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/> - <div> - Show: - <div class="check-element animate-hide" ng-show="checked"> - <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. - </div> - </div> - <div> - Hide: - <div class="check-element animate-hide" ng-hide="checked"> - <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. - </div> - </div> - </file> - <file name="glyphicons.css"> - @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); - </file> - <file name="animations.css"> - .animate-hide { - transition: all linear 0.5s; - line-height: 20px; - opacity: 1; - padding: 10px; - border: 1px solid black; - background: white; - } - - .animate-hide.ng-hide { - line-height: 0; - opacity: 0; - padding: 0 10px; - } - - .check-element { - padding: 10px; - border: 1px solid black; - background: white; - } - </file> - <file name="protractor.js" type="protractor"> - var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); - var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); - - it('should check ng-show / ng-hide', function() { - expect(thumbsUp.isDisplayed()).toBeFalsy(); - expect(thumbsDown.isDisplayed()).toBeTruthy(); - - element(by.model('checked')).click(); - - expect(thumbsUp.isDisplayed()).toBeTruthy(); - expect(thumbsDown.isDisplayed()).toBeFalsy(); - }); - </file> - </example> - */ -var ngHideDirective = ['$animate', function($animate) { - return { - restrict: 'A', - multiElement: true, - link: function(scope, element, attr) { - scope.$watch(attr.ngHide, function ngHideWatchAction(value) { - // The comment inside of the ngShowDirective explains why we add and - // remove a temporary class for the show/hide animation - $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, { - tempClasses: NG_HIDE_IN_PROGRESS_CLASS - }); - }); - } - }; -}]; - -/** - * @ngdoc directive - * @name ngStyle - * @restrict AC - * - * @description - * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. - * - * @element ANY - * @param {expression} ngStyle - * - * {@link guide/expression Expression} which evals to an - * object whose keys are CSS style names and values are corresponding values for those CSS - * keys. - * - * Since some CSS style names are not valid keys for an object, they must be quoted. - * See the 'background-color' style in the example below. - * - * @example - <example> - <file name="index.html"> - <input type="button" value="set color" ng-click="myStyle={color:'red'}"> - <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}"> - <input type="button" value="clear" ng-click="myStyle={}"> - <br/> - <span ng-style="myStyle">Sample Text</span> - <pre>myStyle={{myStyle}}</pre> - </file> - <file name="style.css"> - span { - color: black; - } - </file> - <file name="protractor.js" type="protractor"> - var colorSpan = element(by.css('span')); - - it('should check ng-style', function() { - expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); - element(by.css('input[value=\'set color\']')).click(); - expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); - element(by.css('input[value=clear]')).click(); - expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); - }); - </file> - </example> - */ -var ngStyleDirective = ngDirective(function(scope, element, attr) { - scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { - if (oldStyles && (newStyles !== oldStyles)) { - forEach(oldStyles, function(val, style) { element.css(style, '');}); - } - if (newStyles) element.css(newStyles); - }, true); -}); - -/** - * @ngdoc directive - * @name ngSwitch - * @restrict EA - * - * @description - * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. - * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location - * as specified in the template. - * - * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it - * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element - * matches the value obtained from the evaluated expression. In other words, you define a container element - * (where you place the directive), place an expression on the **`on="..."` attribute** - * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place - * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on - * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default - * attribute is displayed. - * - * <div class="alert alert-info"> - * Be aware that the attribute values to match against cannot be expressions. They are interpreted - * as literal string values to match against. - * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the - * value of the expression `$scope.someVal`. - * </div> - - * @animations - * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container - * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM - * - * @usage - * - * ``` - * <ANY ng-switch="expression"> - * <ANY ng-switch-when="matchValue1">...</ANY> - * <ANY ng-switch-when="matchValue2">...</ANY> - * <ANY ng-switch-default>...</ANY> - * </ANY> - * ``` - * - * - * @scope - * @priority 1200 - * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>. - * On child elements add: - * - * * `ngSwitchWhen`: the case statement to match against. If match then this - * case will be displayed. If the same match appears multiple times, all the - * elements will be displayed. - * * `ngSwitchDefault`: the default case when no other case match. If there - * are multiple default cases, all of them will be displayed when no other - * case match. - * - * - * @example - <example module="switchExample" deps="angular-animate.js" animations="true"> - <file name="index.html"> - <div ng-controller="ExampleController"> - <select ng-model="selection" ng-options="item for item in items"> - </select> - <code>selection={{selection}}</code> - <hr/> - <div class="animate-switch-container" - ng-switch on="selection"> - <div class="animate-switch" ng-switch-when="settings">Settings Div</div> - <div class="animate-switch" ng-switch-when="home">Home Span</div> - <div class="animate-switch" ng-switch-default>default</div> - </div> - </div> - </file> - <file name="script.js"> - angular.module('switchExample', ['ngAnimate']) - .controller('ExampleController', ['$scope', function($scope) { - $scope.items = ['settings', 'home', 'other']; - $scope.selection = $scope.items[0]; - }]); - </file> - <file name="animations.css"> - .animate-switch-container { - position:relative; - background:white; - border:1px solid black; - height:40px; - overflow:hidden; - } - - .animate-switch { - padding:10px; - } - - .animate-switch.ng-animate { - transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - - position:absolute; - top:0; - left:0; - right:0; - bottom:0; - } - - .animate-switch.ng-leave.ng-leave-active, - .animate-switch.ng-enter { - top:-50px; - } - .animate-switch.ng-leave, - .animate-switch.ng-enter.ng-enter-active { - top:0; - } - </file> - <file name="protractor.js" type="protractor"> - var switchElem = element(by.css('[ng-switch]')); - var select = element(by.model('selection')); - - it('should start in settings', function() { - expect(switchElem.getText()).toMatch(/Settings Div/); - }); - it('should change to home', function() { - select.all(by.css('option')).get(1).click(); - expect(switchElem.getText()).toMatch(/Home Span/); - }); - it('should select default', function() { - select.all(by.css('option')).get(2).click(); - expect(switchElem.getText()).toMatch(/default/); - }); - </file> - </example> - */ -var ngSwitchDirective = ['$animate', function($animate) { - return { - require: 'ngSwitch', - - // asks for $scope to fool the BC controller module - controller: ['$scope', function ngSwitchController() { - this.cases = {}; - }], - link: function(scope, element, attr, ngSwitchController) { - var watchExpr = attr.ngSwitch || attr.on, - selectedTranscludes = [], - selectedElements = [], - previousLeaveAnimations = [], - selectedScopes = []; - - var spliceFactory = function(array, index) { - return function() { array.splice(index, 1); }; - }; - - scope.$watch(watchExpr, function ngSwitchWatchAction(value) { - var i, ii; - for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) { - $animate.cancel(previousLeaveAnimations[i]); - } - previousLeaveAnimations.length = 0; - - for (i = 0, ii = selectedScopes.length; i < ii; ++i) { - var selected = getBlockNodes(selectedElements[i].clone); - selectedScopes[i].$destroy(); - var promise = previousLeaveAnimations[i] = $animate.leave(selected); - promise.then(spliceFactory(previousLeaveAnimations, i)); - } - - selectedElements.length = 0; - selectedScopes.length = 0; - - if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { - forEach(selectedTranscludes, function(selectedTransclude) { - selectedTransclude.transclude(function(caseElement, selectedScope) { - selectedScopes.push(selectedScope); - var anchor = selectedTransclude.element; - caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: '); - var block = { clone: caseElement }; - - selectedElements.push(block); - $animate.enter(caseElement, anchor.parent(), anchor); - }); - }); - } - }); - } - }; -}]; - -var ngSwitchWhenDirective = ngDirective({ - transclude: 'element', - priority: 1200, - require: '^ngSwitch', - multiElement: true, - link: function(scope, element, attrs, ctrl, $transclude) { - ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); - ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); - } -}); - -var ngSwitchDefaultDirective = ngDirective({ - transclude: 'element', - priority: 1200, - require: '^ngSwitch', - multiElement: true, - link: function(scope, element, attr, ctrl, $transclude) { - ctrl.cases['?'] = (ctrl.cases['?'] || []); - ctrl.cases['?'].push({ transclude: $transclude, element: element }); - } -}); - -/** - * @ngdoc directive - * @name ngTransclude - * @restrict EAC - * - * @description - * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. - * - * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted. - * - * @element ANY - * - * @example - <example module="transcludeExample"> - <file name="index.html"> - <script> - angular.module('transcludeExample', []) - .directive('pane', function(){ - return { - restrict: 'E', - transclude: true, - scope: { title:'@' }, - template: '<div style="border: 1px solid black;">' + - '<div style="background-color: gray">{{title}}</div>' + - '<ng-transclude></ng-transclude>' + - '</div>' - }; - }) - .controller('ExampleController', ['$scope', function($scope) { - $scope.title = 'Lorem Ipsum'; - $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; - }]); - </script> - <div ng-controller="ExampleController"> - <input ng-model="title" aria-label="title"> <br/> - <textarea ng-model="text" aria-label="text"></textarea> <br/> - <pane title="{{title}}">{{text}}</pane> - </div> - </file> - <file name="protractor.js" type="protractor"> - it('should have transcluded', function() { - var titleElement = element(by.model('title')); - titleElement.clear(); - titleElement.sendKeys('TITLE'); - var textElement = element(by.model('text')); - textElement.clear(); - textElement.sendKeys('TEXT'); - expect(element(by.binding('title')).getText()).toEqual('TITLE'); - expect(element(by.binding('text')).getText()).toEqual('TEXT'); - }); - </file> - </example> - * - */ -var ngTranscludeDirective = ngDirective({ - restrict: 'EAC', - link: function($scope, $element, $attrs, controller, $transclude) { - if (!$transclude) { - throw minErr('ngTransclude')('orphan', - 'Illegal use of ngTransclude directive in the template! ' + - 'No parent directive that requires a transclusion found. ' + - 'Element: {0}', - startingTag($element)); - } - - $transclude(function(clone) { - $element.empty(); - $element.append(clone); - }); - } -}); - -/** - * @ngdoc directive - * @name script - * @restrict E - * - * @description - * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the - * template can be used by {@link ng.directive:ngInclude `ngInclude`}, - * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the - * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be - * assigned through the element's `id`, which can then be used as a directive's `templateUrl`. - * - * @param {string} type Must be set to `'text/ng-template'`. - * @param {string} id Cache name of the template. - * - * @example - <example> - <file name="index.html"> - <script type="text/ng-template" id="/tpl.html"> - Content of the template. - </script> - - <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a> - <div id="tpl-content" ng-include src="currentTpl"></div> - </file> - <file name="protractor.js" type="protractor"> - it('should load template defined inside script tag', function() { - element(by.css('#tpl-link')).click(); - expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); - }); - </file> - </example> - */ -var scriptDirective = ['$templateCache', function($templateCache) { - return { - restrict: 'E', - terminal: true, - compile: function(element, attr) { - if (attr.type == 'text/ng-template') { - var templateUrl = attr.id, - text = element[0].text; - - $templateCache.put(templateUrl, text); - } - } - }; -}]; - -var noopNgModelController = { $setViewValue: noop, $render: noop }; - -/** - * @ngdoc type - * @name select.SelectController - * @description - * The controller for the `<select>` directive. This provides support for reading - * and writing the selected value(s) of the control and also coordinates dynamically - * added `<option>` elements, perhaps by an `ngRepeat` directive. - */ -var SelectController = - ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { - - var self = this, - optionsMap = new HashMap(); - - // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors - self.ngModelCtrl = noopNgModelController; - - // The "unknown" option is one that is prepended to the list if the viewValue - // does not match any of the options. When it is rendered the value of the unknown - // option is '? XXX ?' where XXX is the hashKey of the value that is not known. - // - // We can't just jqLite('<option>') since jqLite is not smart enough - // to create it in <select> and IE barfs otherwise. - self.unknownOption = jqLite(document.createElement('option')); - self.renderUnknownOption = function(val) { - var unknownVal = '? ' + hashKey(val) + ' ?'; - self.unknownOption.val(unknownVal); - $element.prepend(self.unknownOption); - $element.val(unknownVal); - }; - - $scope.$on('$destroy', function() { - // disable unknown option so that we don't do work when the whole select is being destroyed - self.renderUnknownOption = noop; - }); - - self.removeUnknownOption = function() { - if (self.unknownOption.parent()) self.unknownOption.remove(); - }; - - - // Read the value of the select control, the implementation of this changes depending - // upon whether the select can have multiple values and whether ngOptions is at work. - self.readValue = function readSingleValue() { - self.removeUnknownOption(); - return $element.val(); - }; - - - // Write the value to the select control, the implementation of this changes depending - // upon whether the select can have multiple values and whether ngOptions is at work. - self.writeValue = function writeSingleValue(value) { - if (self.hasOption(value)) { - self.removeUnknownOption(); - $element.val(value); - if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy - } else { - if (value == null && self.emptyOption) { - self.removeUnknownOption(); - $element.val(''); - } else { - self.renderUnknownOption(value); - } - } - }; - - - // Tell the select control that an option, with the given value, has been added - self.addOption = function(value, element) { - assertNotHasOwnProperty(value, '"option value"'); - if (value === '') { - self.emptyOption = element; - } - var count = optionsMap.get(value) || 0; - optionsMap.put(value, count + 1); - }; - - // Tell the select control that an option, with the given value, has been removed - self.removeOption = function(value) { - var count = optionsMap.get(value); - if (count) { - if (count === 1) { - optionsMap.remove(value); - if (value === '') { - self.emptyOption = undefined; - } - } else { - optionsMap.put(value, count - 1); - } - } - }; - - // Check whether the select control has an option matching the given value - self.hasOption = function(value) { - return !!optionsMap.get(value); - }; -}]; - -/** - * @ngdoc directive - * @name select - * @restrict E - * - * @description - * HTML `SELECT` element with angular data-binding. - * - * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding - * between the scope and the `<select>` control (including setting default values). - * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or - * {@link ngOptions `ngOptions`} directives. - * - * When an item in the `<select>` menu is selected, the value of the selected option will be bound - * to the model identified by the `ngModel` directive. With static or repeated options, this is - * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing. - * If you want dynamic value attributes, you can use interpolation inside the value attribute. - * - * <div class="alert alert-warning"> - * Note that the value of a `select` directive used without `ngOptions` is always a string. - * When the model needs to be bound to a non-string value, you must either explictly convert it - * using a directive (see example below) or use `ngOptions` to specify the set of options. - * This is because an option element can only be bound to string values at present. - * </div> - * - * If the viewValue of `ngModel` does not match any of the options, then the control - * will automatically add an "unknown" option, which it then removes when the mismatch is resolved. - * - * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can - * be nested into the `<select>` element. This element will then represent the `null` or "not selected" - * option. See example below for demonstration. - * - * <div class="alert alert-info"> - * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions - * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as - * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the - * comprehension expression, and additionally in reducing memory and increasing speed by not creating - * a new scope for each repeated instance. - * </div> - * - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds required attribute and required validation constraint to - * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required - * when you want to data-bind to the required attribute. - * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user - * interaction with the select element. - * @param {string=} ngOptions sets the options that the select is populated with and defines what is - * set on the model on selection. See {@link ngOptions `ngOptions`}. - * - * @example - * ### Simple `select` elements with static options - * - * <example name="static-select" module="staticSelect"> - * <file name="index.html"> - * <div ng-controller="ExampleController"> - * <form name="myForm"> - * <label for="singleSelect"> Single select: </label><br> - * <select name="singleSelect" ng-model="data.singleSelect"> - * <option value="option-1">Option 1</option> - * <option value="option-2">Option 2</option> - * </select><br> - * - * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br> - * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect"> - * <option value="">---Please select---</option> <!-- not selected / blank option --> - * <option value="{{data.option1}}">Option 1</option> <!-- interpolation --> - * <option value="option-2">Option 2</option> - * </select><br> - * <button ng-click="forceUnknownOption()">Force unknown option</button><br> - * <tt>singleSelect = {{data.singleSelect}}</tt> - * - * <hr> - * <label for="multipleSelect"> Multiple select: </label><br> - * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple> - * <option value="option-1">Option 1</option> - * <option value="option-2">Option 2</option> - * <option value="option-3">Option 3</option> - * </select><br> - * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/> - * </form> - * </div> - * </file> - * <file name="app.js"> - * angular.module('staticSelect', []) - * .controller('ExampleController', ['$scope', function($scope) { - * $scope.data = { - * singleSelect: null, - * multipleSelect: [], - * option1: 'option-1', - * }; - * - * $scope.forceUnknownOption = function() { - * $scope.data.singleSelect = 'nonsense'; - * }; - * }]); - * </file> - *</example> - * - * ### Using `ngRepeat` to generate `select` options - * <example name="ngrepeat-select" module="ngrepeatSelect"> - * <file name="index.html"> - * <div ng-controller="ExampleController"> - * <form name="myForm"> - * <label for="repeatSelect"> Repeat select: </label> - * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect"> - * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option> - * </select> - * </form> - * <hr> - * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/> - * </div> - * </file> - * <file name="app.js"> - * angular.module('ngrepeatSelect', []) - * .controller('ExampleController', ['$scope', function($scope) { - * $scope.data = { - * repeatSelect: null, - * availableOptions: [ - * {id: '1', name: 'Option A'}, - * {id: '2', name: 'Option B'}, - * {id: '3', name: 'Option C'} - * ], - * }; - * }]); - * </file> - *</example> - * - * - * ### Using `select` with `ngOptions` and setting a default value - * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples. - * - * <example name="select-with-default-values" module="defaultValueSelect"> - * <file name="index.html"> - * <div ng-controller="ExampleController"> - * <form name="myForm"> - * <label for="mySelect">Make a choice:</label> - * <select name="mySelect" id="mySelect" - * ng-options="option.name for option in data.availableOptions track by option.id" - * ng-model="data.selectedOption"></select> - * </form> - * <hr> - * <tt>option = {{data.selectedOption}}</tt><br/> - * </div> - * </file> - * <file name="app.js"> - * angular.module('defaultValueSelect', []) - * .controller('ExampleController', ['$scope', function($scope) { - * $scope.data = { - * availableOptions: [ - * {id: '1', name: 'Option A'}, - * {id: '2', name: 'Option B'}, - * {id: '3', name: 'Option C'} - * ], - * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui - * }; - * }]); - * </file> - *</example> - * - * - * ### Binding `select` to a non-string value via `ngModel` parsing / formatting - * - * <example name="select-with-non-string-options" module="nonStringSelect"> - * <file name="index.html"> - * <select ng-model="model.id" convert-to-number> - * <option value="0">Zero</option> - * <option value="1">One</option> - * <option value="2">Two</option> - * </select> - * {{ model }} - * </file> - * <file name="app.js"> - * angular.module('nonStringSelect', []) - * .run(function($rootScope) { - * $rootScope.model = { id: 2 }; - * }) - * .directive('convertToNumber', function() { - * return { - * require: 'ngModel', - * link: function(scope, element, attrs, ngModel) { - * ngModel.$parsers.push(function(val) { - * return parseInt(val, 10); - * }); - * ngModel.$formatters.push(function(val) { - * return '' + val; - * }); - * } - * }; - * }); - * </file> - * <file name="protractor.js" type="protractor"> - * it('should initialize to model', function() { - * var select = element(by.css('select')); - * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two'); - * }); - * </file> - * </example> - * - */ -var selectDirective = function() { - - return { - restrict: 'E', - require: ['select', '?ngModel'], - controller: SelectController, - link: function(scope, element, attr, ctrls) { - - // if ngModel is not defined, we don't need to do anything - var ngModelCtrl = ctrls[1]; - if (!ngModelCtrl) return; - - var selectCtrl = ctrls[0]; - - selectCtrl.ngModelCtrl = ngModelCtrl; - - // We delegate rendering to the `writeValue` method, which can be changed - // if the select can have multiple selected values or if the options are being - // generated by `ngOptions` - ngModelCtrl.$render = function() { - selectCtrl.writeValue(ngModelCtrl.$viewValue); - }; - - // When the selected item(s) changes we delegate getting the value of the select control - // to the `readValue` method, which can be changed if the select can have multiple - // selected values or if the options are being generated by `ngOptions` - element.on('change', function() { - scope.$apply(function() { - ngModelCtrl.$setViewValue(selectCtrl.readValue()); - }); - }); - - // If the select allows multiple values then we need to modify how we read and write - // values from and to the control; also what it means for the value to be empty and - // we have to add an extra watch since ngModel doesn't work well with arrays - it - // doesn't trigger rendering if only an item in the array changes. - if (attr.multiple) { - - // Read value now needs to check each option to see if it is selected - selectCtrl.readValue = function readMultipleValue() { - var array = []; - forEach(element.find('option'), function(option) { - if (option.selected) { - array.push(option.value); - } - }); - return array; - }; - - // Write value now needs to set the selected property of each matching option - selectCtrl.writeValue = function writeMultipleValue(value) { - var items = new HashMap(value); - forEach(element.find('option'), function(option) { - option.selected = isDefined(items.get(option.value)); - }); - }; - - // we have to do it on each watch since ngModel watches reference, but - // we need to work of an array, so we need to see if anything was inserted/removed - var lastView, lastViewRef = NaN; - scope.$watch(function selectMultipleWatch() { - if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) { - lastView = shallowCopy(ngModelCtrl.$viewValue); - ngModelCtrl.$render(); - } - lastViewRef = ngModelCtrl.$viewValue; - }); - - // If we are a multiple select then value is now a collection - // so the meaning of $isEmpty changes - ngModelCtrl.$isEmpty = function(value) { - return !value || value.length === 0; - }; - - } - } - }; -}; - - -// The option directive is purely designed to communicate the existence (or lack of) -// of dynamically created (and destroyed) option elements to their containing select -// directive via its controller. -var optionDirective = ['$interpolate', function($interpolate) { - - function chromeHack(optionElement) { - // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459 - // Adding an <option selected="selected"> element to a <select required="required"> should - // automatically select the new element - if (optionElement[0].hasAttribute('selected')) { - optionElement[0].selected = true; - } - } - - return { - restrict: 'E', - priority: 100, - compile: function(element, attr) { - - if (isDefined(attr.value)) { - // If the value attribute is defined, check if it contains an interpolation - var valueInterpolated = $interpolate(attr.value, true); - } else { - // If the value attribute is not defined then we fall back to the - // text content of the option element, which may be interpolated - var interpolateFn = $interpolate(element.text(), true); - if (!interpolateFn) { - attr.$set('value', element.text()); - } - } - - return function(scope, element, attr) { - - // This is an optimization over using ^^ since we don't want to have to search - // all the way to the root of the DOM for every single option element - var selectCtrlName = '$selectController', - parent = element.parent(), - selectCtrl = parent.data(selectCtrlName) || - parent.parent().data(selectCtrlName); // in case we are in optgroup - - function addOption(optionValue) { - selectCtrl.addOption(optionValue, element); - selectCtrl.ngModelCtrl.$render(); - chromeHack(element); - } - - // Only update trigger option updates if this is an option within a `select` - // that also has `ngModel` attached - if (selectCtrl && selectCtrl.ngModelCtrl) { - - if (valueInterpolated) { - // The value attribute is interpolated - var oldVal; - attr.$observe('value', function valueAttributeObserveAction(newVal) { - if (isDefined(oldVal)) { - selectCtrl.removeOption(oldVal); - } - oldVal = newVal; - addOption(newVal); - }); - } else if (interpolateFn) { - // The text content is interpolated - scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) { - attr.$set('value', newVal); - if (oldVal !== newVal) { - selectCtrl.removeOption(oldVal); - } - addOption(newVal); - }); - } else { - // The value attribute is static - addOption(attr.value); - } - - element.on('$destroy', function() { - selectCtrl.removeOption(attr.value); - selectCtrl.ngModelCtrl.$render(); - }); - } - }; - } - }; -}]; - -var styleDirective = valueFn({ - restrict: 'E', - terminal: false -}); - -var requiredDirective = function() { - return { - restrict: 'A', - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - attr.required = true; // force truthy in case we are on non input element - - ctrl.$validators.required = function(modelValue, viewValue) { - return !attr.required || !ctrl.$isEmpty(viewValue); - }; - - attr.$observe('required', function() { - ctrl.$validate(); - }); - } - }; -}; - - -var patternDirective = function() { - return { - restrict: 'A', - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - - var regexp, patternExp = attr.ngPattern || attr.pattern; - attr.$observe('pattern', function(regex) { - if (isString(regex) && regex.length > 0) { - regex = new RegExp('^' + regex + '$'); - } - - if (regex && !regex.test) { - throw minErr('ngPattern')('noregexp', - 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, - regex, startingTag(elm)); - } - - regexp = regex || undefined; - ctrl.$validate(); - }); - - ctrl.$validators.pattern = function(modelValue, viewValue) { - // HTML5 pattern constraint validates the input value, so we validate the viewValue - return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue); - }; - } - }; -}; - - -var maxlengthDirective = function() { - return { - restrict: 'A', - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - - var maxlength = -1; - attr.$observe('maxlength', function(value) { - var intVal = toInt(value); - maxlength = isNaN(intVal) ? -1 : intVal; - ctrl.$validate(); - }); - ctrl.$validators.maxlength = function(modelValue, viewValue) { - return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength); - }; - } - }; -}; - -var minlengthDirective = function() { - return { - restrict: 'A', - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - - var minlength = 0; - attr.$observe('minlength', function(value) { - minlength = toInt(value) || 0; - ctrl.$validate(); - }); - ctrl.$validators.minlength = function(modelValue, viewValue) { - return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; - }; - } - }; -}; - -if (window.angular.bootstrap) { - //AngularJS is already loaded, so we can return here... - console.log('WARNING: Tried to load angular more than once.'); - return; -} - -//try to bind to jquery now so that one can write jqLite(document).ready() -//but we will rebind on bootstrap again. -bindJQuery(); - -publishExternalAPI(angular); - -angular.module("ngLocale", [], ["$provide", function($provide) { -var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; -function getDecimals(n) { - n = n + ''; - var i = n.indexOf('.'); - return (i == -1) ? 0 : n.length - i - 1; -} - -function getVF(n, opt_precision) { - var v = opt_precision; - - if (undefined === v) { - v = Math.min(getDecimals(n), 3); - } - - var base = Math.pow(10, v); - var f = ((n * base) | 0) % base; - return {v: v, f: f}; -} - -$provide.value("$locale", { - "DATETIME_FORMATS": { - "AMPMS": [ - "AM", - "PM" - ], - "DAY": [ - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday" - ], - "ERANAMES": [ - "Before Christ", - "Anno Domini" - ], - "ERAS": [ - "BC", - "AD" - ], - "FIRSTDAYOFWEEK": 6, - "MONTH": [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - ], - "SHORTDAY": [ - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - ], - "SHORTMONTH": [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - ], - "WEEKENDRANGE": [ - 5, - 6 - ], - "fullDate": "EEEE, MMMM d, y", - "longDate": "MMMM d, y", - "medium": "MMM d, y h:mm:ss a", - "mediumDate": "MMM d, y", - "mediumTime": "h:mm:ss a", - "short": "M/d/yy h:mm a", - "shortDate": "M/d/yy", - "shortTime": "h:mm a" - }, - "NUMBER_FORMATS": { - "CURRENCY_SYM": "$", - "DECIMAL_SEP": ".", - "GROUP_SEP": ",", - "PATTERNS": [ - { - "gSize": 3, - "lgSize": 3, - "maxFrac": 3, - "minFrac": 0, - "minInt": 1, - "negPre": "-", - "negSuf": "", - "posPre": "", - "posSuf": "" - }, - { - "gSize": 3, - "lgSize": 3, - "maxFrac": 2, - "minFrac": 2, - "minInt": 1, - "negPre": "-\u00a4", - "negSuf": "", - "posPre": "\u00a4", - "posSuf": "" - } - ] - }, - "id": "en-us", - "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} -}); -}]); - - jqLite(document).ready(function() { - angularInit(document, bootstrap); - }); - -})(window, document); - -!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>'); \ No newline at end of file diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js index 272101ec7cb75cc815ad1d07c316c711ce96c09c..b10e3e9776953174cf678d4689d71ab6dbe9698f 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js @@ -1,294 +1,314 @@ /* - AngularJS v1.4.7 - (c) 2010-2015 Google, Inc. http://angularjs.org + AngularJS v1.5.5 + (c) 2010-2016 Google, Inc. http://angularjs.org License: MIT */ -(function(Q,X,w){'use strict';function I(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.4.7/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var d=encodeURIComponent,e;e=arguments[a];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return Error(c)}}function Da(b){if(null==b||Za(b))return!1;var a="length"in Object(b)&&b.length; -return b.nodeType===pa&&a?!0:G(b)||J(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function m(b,a,c){var d,e;if(b)if(x(b))for(d in b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d)||a.call(c,b[d],d,b);else if(J(b)||Da(b)){var f="object"!==typeof b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else if(b.forEach&&b.forEach!==m)b.forEach(a,c,b);else if(mc(b))for(d in b)a.call(c,b[d],d,b);else if("function"===typeof b.hasOwnProperty)for(d in b)b.hasOwnProperty(d)&& -a.call(c,b[d],d,b);else for(d in b)ta.call(b,d)&&a.call(c,b[d],d,b);return b}function nc(b,a,c){for(var d=Object.keys(b).sort(),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function oc(b){return function(a,c){b(c,a)}}function Ud(){return++nb}function pc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function Mb(b,a,c){for(var d=b.$$hashKey,e=0,f=a.length;e<f;++e){var h=a[e];if(C(h)||x(h))for(var g=Object.keys(h),l=0,k=g.length;l<k;l++){var n=g[l],p=h[n];c&&C(p)?ea(p)?b[n]=new Date(p.valueOf()):Oa(p)? -b[n]=new RegExp(p):(C(b[n])||(b[n]=J(p)?[]:{}),Mb(b[n],[p],!0)):b[n]=p}}pc(b,d);return b}function P(b){return Mb(b,ua.call(arguments,1),!1)}function Vd(b){return Mb(b,ua.call(arguments,1),!0)}function Y(b){return parseInt(b,10)}function Nb(b,a){return P(Object.create(b),a)}function y(){}function $a(b){return b}function qa(b){return function(){return b}}function qc(b){return x(b.toString)&&b.toString!==Object.prototype.toString}function v(b){return"undefined"===typeof b}function A(b){return"undefined"!== -typeof b}function C(b){return null!==b&&"object"===typeof b}function mc(b){return null!==b&&"object"===typeof b&&!rc(b)}function G(b){return"string"===typeof b}function V(b){return"number"===typeof b}function ea(b){return"[object Date]"===va.call(b)}function x(b){return"function"===typeof b}function Oa(b){return"[object RegExp]"===va.call(b)}function Za(b){return b&&b.window===b}function ab(b){return b&&b.$evalAsync&&b.$watch}function bb(b){return"boolean"===typeof b}function sc(b){return!(!b||!(b.nodeName|| -b.prop&&b.attr&&b.find))}function Wd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function wa(b){return F(b.nodeName||b[0]&&b[0].nodeName)}function cb(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return c}function ha(b,a,c,d){if(Za(b)||ab(b))throw Ea("cpws");if(tc.test(va.call(a)))throw Ea("cpta");if(a){if(b===a)throw Ea("cpi");c=c||[];d=d||[];C(b)&&(c.push(b),d.push(a));var e;if(J(b))for(e=a.length=0;e<b.length;e++)a.push(ha(b[e],null,c,d));else{var f=a.$$hashKey;J(a)? -a.length=0:m(a,function(b,c){delete a[c]});if(mc(b))for(e in b)a[e]=ha(b[e],null,c,d);else if(b&&"function"===typeof b.hasOwnProperty)for(e in b)b.hasOwnProperty(e)&&(a[e]=ha(b[e],null,c,d));else for(e in b)ta.call(b,e)&&(a[e]=ha(b[e],null,c,d));pc(a,f)}}else if(a=b,C(b)){if(c&&-1!==(f=c.indexOf(b)))return d[f];if(J(b))return ha(b,[],c,d);if(tc.test(va.call(b)))a=new b.constructor(b);else if(ea(b))a=new Date(b.getTime());else if(Oa(b))a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex= -b.lastIndex;else if(x(b.cloneNode))a=b.cloneNode(!0);else return e=Object.create(rc(b)),ha(b,e,c,d);d&&(c.push(b),d.push(a))}return a}function ja(b,a){if(J(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(C(b))for(c in a=a||{},b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ka(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(J(b)){if(!J(a))return!1;if((c=b.length)==a.length){for(d=0;d< -c;d++)if(!ka(b[d],a[d]))return!1;return!0}}else{if(ea(b))return ea(a)?ka(b.getTime(),a.getTime()):!1;if(Oa(b))return Oa(a)?b.toString()==a.toString():!1;if(ab(b)||ab(a)||Za(b)||Za(a)||J(a)||ea(a)||Oa(a))return!1;c=fa();for(d in b)if("$"!==d.charAt(0)&&!x(b[d])){if(!ka(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!(d in c)&&"$"!==d.charAt(0)&&A(a[d])&&!x(a[d]))return!1;return!0}return!1}function db(b,a,c){return b.concat(ua.call(a,c))}function uc(b,a){var c=2<arguments.length?ua.call(arguments,2):[]; -return!x(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,db(c,arguments,0)):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Xd(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)&&"$"===b.charAt(1)?c=w:Za(a)?c="$WINDOW":a&&X===a?c="$DOCUMENT":ab(a)&&(c="$SCOPE");return c}function eb(b,a){if("undefined"===typeof b)return w;V(a)||(a=a?2:null);return JSON.stringify(b,Xd,a)}function vc(b){return G(b)?JSON.parse(b):b}function wc(b, -a){var c=Date.parse("Jan 01, 1970 00:00:00 "+b)/6E4;return isNaN(c)?a:c}function Ob(b,a,c){c=c?-1:1;var d=wc(a,b.getTimezoneOffset());a=b;b=c*(d-b.getTimezoneOffset());a=new Date(a.getTime());a.setMinutes(a.getMinutes()+b);return a}function xa(b){b=B(b).clone();try{b.empty()}catch(a){}var c=B("<div>").append(b).html();try{return b[0].nodeType===Pa?F(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+F(b)})}catch(d){return F(c)}}function xc(b){try{return decodeURIComponent(b)}catch(a){}} -function yc(b){var a={};m((b||"").split("&"),function(b){var d,e,f;b&&(e=b=b.replace(/\+/g,"%20"),d=b.indexOf("="),-1!==d&&(e=b.substring(0,d),f=b.substring(d+1)),e=xc(e),A(e)&&(f=A(f)?xc(f):!0,ta.call(a,e)?J(a[e])?a[e].push(f):a[e]=[a[e],f]:a[e]=f))});return a}function Pb(b){var a=[];m(b,function(b,d){J(b)?m(b,function(b){a.push(la(d,!0)+(!0===b?"":"="+la(b,!0)))}):a.push(la(d,!0)+(!0===b?"":"="+la(b,!0)))});return a.length?a.join("&"):""}function ob(b){return la(b,!0).replace(/%26/gi,"&").replace(/%3D/gi, -"=").replace(/%2B/gi,"+")}function la(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Yd(b,a){var c,d,e=Qa.length;for(d=0;d<e;++d)if(c=Qa[d]+a,G(c=b.getAttribute(c)))return c;return null}function Zd(b,a){var c,d,e={};m(Qa,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});m(Qa,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":", -"\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==Yd(c,"strict-di"),a(c,d?[d]:[],e))}function zc(b,a,c){C(c)||(c={});c=P({strictDi:!1},c);var d=function(){b=B(b);if(b.injector()){var d=b[0]===X?"document":xa(b);throw Ea("btstrpd",d.replace(/</,"<").replace(/>/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=fb(a,c.strictDi);d.invoke(["$rootScope", -"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;Q&&e.test(Q.name)&&(c.debugInfoEnabled=!0,Q.name=Q.name.replace(e,""));if(Q&&!f.test(Q.name))return d();Q.name=Q.name.replace(f,"");da.resumeBootstrap=function(b){m(b,function(b){a.push(b)});return d()};x(da.resumeDeferredBootstrap)&&da.resumeDeferredBootstrap()}function $d(){Q.name="NG_ENABLE_DEBUG_INFO!"+Q.name;Q.location.reload()} -function ae(b){b=da.element(b).injector();if(!b)throw Ea("test");return b.get("$$testability")}function Ac(b,a){a=a||"_";return b.replace(be,function(b,d){return(d?a:"")+b.toLowerCase()})}function ce(){var b;if(!Bc){var a=pb();(ra=v(a)?Q.jQuery:a?Q[a]:w)&&ra.fn.on?(B=ra,P(ra.fn,{scope:Ra.scope,isolateScope:Ra.isolateScope,controller:Ra.controller,injector:Ra.injector,inheritedData:Ra.inheritedData}),b=ra.cleanData,ra.cleanData=function(a){var d;if(Qb)Qb=!1;else for(var e=0,f;null!=(f=a[e]);e++)(d= -ra._data(f,"events"))&&d.$destroy&&ra(f).triggerHandler("$destroy");b(a)}):B=R;da.element=B;Bc=!0}}function qb(b,a,c){if(!b)throw Ea("areq",a||"?",c||"required");return b}function Sa(b,a,c){c&&J(b)&&(b=b[b.length-1]);qb(x(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ta(b,a){if("hasOwnProperty"===b)throw Ea("badname",a);}function Cc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,h=0;h<f;h++)d=a[h],b&&(b=(e=b)[d]);return!c&& -x(b)?uc(e,b):b}function rb(b){for(var a=b[0],c=b[b.length-1],d,e=1;a!==c&&(a=a.nextSibling);e++)if(d||b[e]!==a)d||(d=B(ua.call(b,0,e))),d.push(a);return d||b}function fa(){return Object.create(null)}function de(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=I("$injector"),d=I("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||I;return a(b,"module",function(){var b={};return function(f,h,g){if("hasOwnProperty"===f)throw d("badname","module");h&&b.hasOwnProperty(f)&&(b[f]=null);return a(b,f,function(){function a(b, -c,e,f){f||(f=d);return function(){f[e||"push"]([b,c,arguments]);return E}}function b(a,c){return function(b,e){e&&x(e)&&(e.$$moduleName=f);d.push([a,c,arguments]);return E}}if(!h)throw c("nomod",f);var d=[],e=[],r=[],t=a("$injector","invoke","push",e),E={_invokeQueue:d,_configBlocks:e,_runBlocks:r,requires:h,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide", -"decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:t,run:function(a){r.push(a);return this}};g&&t(g);return E})}})}function ee(b){P(b,{bootstrap:zc,copy:ha,extend:P,merge:Vd,equals:ka,element:B,forEach:m,injector:fb,noop:y,bind:uc,toJson:eb,fromJson:vc,identity:$a,isUndefined:v,isDefined:A,isString:G,isFunction:x,isObject:C,isNumber:V,isElement:sc,isArray:J, -version:fe,isDate:ea,lowercase:F,uppercase:sb,callbacks:{counter:0},getTestability:ae,$$minErr:I,$$csp:Fa,reloadWithDebugInfo:$d});Rb=de(Q);Rb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:ge});a.provider("$compile",Dc).directive({a:he,input:Ec,textarea:Ec,form:ie,script:je,select:ke,style:le,option:me,ngBind:ne,ngBindHtml:oe,ngBindTemplate:pe,ngClass:qe,ngClassEven:re,ngClassOdd:se,ngCloak:te,ngController:ue,ngForm:ve,ngHide:we,ngIf:xe,ngInclude:ye,ngInit:ze,ngNonBindable:Ae, -ngPluralize:Be,ngRepeat:Ce,ngShow:De,ngStyle:Ee,ngSwitch:Fe,ngSwitchWhen:Ge,ngSwitchDefault:He,ngOptions:Ie,ngTransclude:Je,ngModel:Ke,ngList:Le,ngChange:Me,pattern:Fc,ngPattern:Fc,required:Gc,ngRequired:Gc,minlength:Hc,ngMinlength:Hc,maxlength:Ic,ngMaxlength:Ic,ngValue:Ne,ngModelOptions:Oe}).directive({ngInclude:Pe}).directive(tb).directive(Jc);a.provider({$anchorScroll:Qe,$animate:Re,$animateCss:Se,$$animateQueue:Te,$$AnimateRunner:Ue,$browser:Ve,$cacheFactory:We,$controller:Xe,$document:Ye,$exceptionHandler:Ze, -$filter:Kc,$$forceReflow:$e,$interpolate:af,$interval:bf,$http:cf,$httpParamSerializer:df,$httpParamSerializerJQLike:ef,$httpBackend:ff,$xhrFactory:gf,$location:hf,$log:jf,$parse:kf,$rootScope:lf,$q:mf,$$q:nf,$sce:of,$sceDelegate:pf,$sniffer:qf,$templateCache:rf,$templateRequest:sf,$$testability:tf,$timeout:uf,$window:vf,$$rAF:wf,$$jqLite:xf,$$HashMap:yf,$$cookieReader:zf})}])}function gb(b){return b.replace(Af,function(a,b,d,e){return e?d.toUpperCase():d}).replace(Bf,"Moz$1")}function Lc(b){b=b.nodeType; -return b===pa||!b||9===b}function Mc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Sb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(Cf.exec(b)||["",""])[1].toLowerCase();d=ma[d]||ma._default;c.innerHTML=d[1]+b.replace(Df,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=db(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";m(f,function(a){e.appendChild(a)});return e}function R(b){if(b instanceof R)return b;var a;G(b)&&(b=T(b), -a=!0);if(!(this instanceof R)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new R(b)}if(a){a=X;var c;b=(c=Ef.exec(b))?[a.createElement(c[1])]:(c=Mc(b,a))?c.childNodes:[]}Nc(this,b)}function Ub(b){return b.cloneNode(!0)}function ub(b,a){a||vb(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)vb(c[d])}function Oc(b,a,c,d){if(A(d))throw Tb("offargs");var e=(d=wb(b))&&d.events,f=d&&d.handle;if(f)if(a)m(a.split(" "),function(a){if(A(c)){var d=e[a];cb(d||[],c);if(d&&0< -d.length)return}b.removeEventListener(a,f,!1);delete e[a]});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,f,!1),delete e[a]}function vb(b,a){var c=b.ng339,d=c&&hb[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Oc(b)),delete hb[c],b.ng339=w))}function wb(b,a){var c=b.ng339,c=c&&hb[c];a&&!c&&(b.ng339=c=++Ff,c=hb[c]={events:{},data:{},handle:w});return c}function Vb(b,a,c){if(Lc(b)){var d=A(c),e=!d&&a&&!C(a),f=!a;b=(b=wb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b; -if(e)return b&&b[a];P(b,a)}}}function xb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function yb(b,a){a&&b.setAttribute&&m(a.split(" "),function(a){b.setAttribute("class",T((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+T(a)+" "," ")))})}function zb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");m(a.split(" "),function(a){a=T(a);-1===c.indexOf(" "+a+" ")&& -(c+=a+" ")});b.setAttribute("class",T(c))}}function Nc(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var c=a.length;if("number"===typeof c&&a.window!==a){if(c)for(var d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function Pc(b,a){return Ab(b,"$"+(a||"ngController")+"Controller")}function Ab(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=J(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if(A(c=B.data(b,a[d])))return c;b=b.parentNode||11===b.nodeType&&b.host}}function Qc(b){for(ub(b,!0);b.firstChild;)b.removeChild(b.firstChild)} -function Wb(b,a){a||ub(b);var c=b.parentNode;c&&c.removeChild(b)}function Gf(b,a){a=a||Q;if("complete"===a.document.readyState)a.setTimeout(b);else B(a).on("load",b)}function Rc(b,a){var c=Bb[a.toLowerCase()];return c&&Sc[wa(b)]&&c}function Hf(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=a[e||c.type],h=f?f.length:0;if(h){if(v(c.immediatePropagationStopped)){var g=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped= -!0;c.stopPropagation&&c.stopPropagation();g&&g.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};1<h&&(f=ja(f));for(var l=0;l<h;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return c}function xf(){this.$get=function(){return P(R,{hasClass:function(b,a){b.attr&&(b=b[0]);return xb(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return zb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return yb(b,a)}})}}function Ga(b,a){var c=b&&b.$$hashKey; -if(c)return"function"===typeof c&&(c=b.$$hashKey()),c;c=typeof b;return c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||Ud)():c+":"+b}function Ua(b,a){if(a){var c=0;this.nextUid=function(){return++c}}m(b,this.put,this)}function If(b){return(b=b.toString().replace(Tc,"").match(Uc))?"function("+(b[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function fb(b,a){function c(a){return function(b,c){if(C(b))m(b,oc(a));else return a(b,c)}}function d(a,b){Ta(a,"service");if(x(b)||J(b))b=r.instantiate(b); -if(!b.$get)throw Ha("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var c=E.invoke(b,this);if(v(c))throw Ha("undef",a);return c}}function f(a,b,c){return d(a,{$get:!1!==c?e(a,b):b})}function h(a){qb(v(a)||J(a),"modulesToLoad","not an array");var b=[],c;m(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=r.get(e[0]);f[e[1]].apply(f,e[2])}}if(!n.get(a)){n.put(a,!0);try{G(a)?(c=Rb(a),b=b.concat(h(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)): -x(a)?b.push(r.invoke(a)):J(a)?b.push(r.invoke(a)):Sa(a,"module")}catch(e){throw J(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ha("modulerr",a,e.stack||e.message||e);}}});return b}function g(b,c){function d(a,e){if(b.hasOwnProperty(a)){if(b[a]===l)throw Ha("cdep",a+" <- "+k.join(" <- "));return b[a]}try{return k.unshift(a),b[a]=l,b[a]=c(a,e)}catch(f){throw b[a]===l&&delete b[a],f;}finally{k.shift()}}function e(b,c,f,g){"string"===typeof f&&(g= -f,f=null);var h=[],k=fb.$$annotate(b,a,g),l,r,p;r=0;for(l=k.length;r<l;r++){p=k[r];if("string"!==typeof p)throw Ha("itkn",p);h.push(f&&f.hasOwnProperty(p)?f[p]:d(p,g))}J(b)&&(b=b[l]);return b.apply(c,h)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((J(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return C(a)||x(a)?a:d},get:d,annotate:fb.$$annotate,has:function(a){return p.hasOwnProperty(a+"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new Ua([],!0),p={$provide:{provider:c(d), -factory:c(f),service:c(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return f(a,qa(b),!1)}),constant:c(function(a,b){Ta(a,"constant");p[a]=b;t[a]=b}),decorator:function(a,b){var c=r.get(a+"Provider"),d=c.$get;c.$get=function(){var a=E.invoke(d,c);return E.invoke(b,null,{$delegate:a})}}}},r=p.$injector=g(p,function(a,b){da.isString(b)&&k.push(b);throw Ha("unpr",k.join(" <- "));}),t={},E=t.$injector=g(t,function(a,b){var c=r.get(a+"Provider",b); -return E.invoke(c.$get,c,w,a)});m(h(b),function(a){a&&E.invoke(a)});return E}function Qe(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===wa(a))return b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var c;c=h.yOffset;x(c)?c=c():sc(c)?(c=c[0],c="fixed"!==a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):V(c)||(c=0);c&&(b=b.getBoundingClientRect().top, -a.scrollBy(0,b-c))}else a.scrollTo(0,0)}function h(a){a=G(a)?a:c.hash();var b;a?(b=g.getElementById(a))?f(b):(b=e(g.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(a,b){a===b&&""===a||Gf(function(){d.$evalAsync(h)})});return h}]}function ib(b,a){if(!b&&!a)return"";if(!b)return a;if(!a)return b;J(b)&&(b=b.join(" "));J(a)&&(a=a.join(" "));return b+" "+a}function Jf(b){G(b)&&(b=b.split(" "));var a=fa();m(b,function(b){b.length&& -(a[b]=!0)});return a}function Ia(b){return C(b)?b:{}}function Kf(b,a,c,d){function e(a){try{a.apply(null,ua.call(arguments,1))}finally{if(E--,0===E)for(;K.length;)try{K.pop()()}catch(b){c.error(b)}}}function f(){ia=null;h();g()}function h(){a:{try{u=n.state;break a}catch(a){}u=void 0}u=v(u)?null:u;ka(u,L)&&(u=L);L=u}function g(){if(z!==l.url()||q!==u)z=l.url(),q=u,m(O,function(a){a(l.url(),u)})}var l=this,k=b.location,n=b.history,p=b.setTimeout,r=b.clearTimeout,t={};l.isMock=!1;var E=0,K=[];l.$$completeOutstandingRequest= -e;l.$$incOutstandingRequestCount=function(){E++};l.notifyWhenNoOutstandingRequests=function(a){0===E?a():K.push(a)};var u,q,z=k.href,N=a.find("base"),ia=null;h();q=u;l.url=function(a,c,e){v(e)&&(e=null);k!==b.location&&(k=b.location);n!==b.history&&(n=b.history);if(a){var f=q===e;if(z===a&&(!d.history||f))return l;var g=z&&Ja(z)===Ja(a);z=a;q=e;if(!d.history||g&&f){if(!g||ia)ia=a;c?k.replace(a):g?(c=k,e=a.indexOf("#"),e=-1===e?"":a.substr(e),c.hash=e):k.href=a;k.href!==a&&(ia=a)}else n[c?"replaceState": -"pushState"](e,"",a),h(),q=u;return l}return ia||k.href.replace(/%27/g,"'")};l.state=function(){return u};var O=[],H=!1,L=null;l.onUrlChange=function(a){if(!H){if(d.history)B(b).on("popstate",f);B(b).on("hashchange",f);H=!0}O.push(a);return a};l.$$applicationDestroyed=function(){B(b).off("hashchange popstate",f)};l.$$checkUrlChange=g;l.baseHref=function(){var a=N.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};l.defer=function(a,b){var c;E++;c=p(function(){delete t[c];e(a)},b||0); -t[c]=!0;return c};l.defer.cancel=function(a){return t[a]?(delete t[a],r(a),e(y),!0):!1}}function Ve(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new Kf(b,d,a,c)}]}function We(){this.$get=function(){function b(b,d){function e(a){a!=p&&(r?r==a&&(r=a.n):r=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw I("$cacheFactory")("iid",b);var h=0,g=P({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,r=null;return a[b]= -{put:function(a,b){if(!v(b)){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}a in l||h++;l[a]=b;h>k&&this.remove(r.key);return b}},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==p&&(p=b.p);b==r&&(r=b.n);f(b.n,b.p);delete n[a]}delete l[a];h--},removeAll:function(){l={};h=0;n={};p=r=null},destroy:function(){n=g=l=null;delete a[b]},info:function(){return P({},g,{size:h})}}}var a={};b.info=function(){var b= -{};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function rf(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Dc(b,a){function c(a,b,c){var d=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,e={};m(a,function(a,f){var g=a.match(d);if(!g)throw ga("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f}});return e}function d(a){var b=a.charAt(0);if(!b|| -b!==F(b))throw ga("baddir",a);if(a!==a.trim())throw ga("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,h=/(([\w\-]+)(?:\:([^;]+))?;?)/,g=Wd("ngSrc,ngSrcset,src,srcset"),l=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,k=/^(on[a-z]+|formaction)$/;this.directive=function r(a,f){Ta(a,"directive");G(a)?(d(a),qb(f,"directiveFactory"),e.hasOwnProperty(a)||(e[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,d){var f=[];m(e[a],function(e,g){try{var h=b.invoke(e);x(h)?h={compile:qa(h)}: -!h.compile&&h.link&&(h.compile=qa(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||a;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";var k=h,l=h,r=h.name,n={isolateScope:null,bindToController:null};C(l.scope)&&(!0===l.bindToController?(n.bindToController=c(l.scope,r,!0),n.isolateScope={}):n.isolateScope=c(l.scope,r,!1));C(l.bindToController)&&(n.bindToController=c(l.bindToController,r,!0));if(C(n.bindToController)){var S=l.controller,E=l.controllerAs;if(!S)throw ga("noctrl", -r);var ca;a:if(E&&G(E))ca=E;else{if(G(S)){var m=Vc.exec(S);if(m){ca=m[3];break a}}ca=void 0}if(!ca)throw ga("noident",r);}var s=k.$$bindings=n;C(s.isolateScope)&&(h.$$isolateBindings=s.isolateScope);h.$$moduleName=e.$$moduleName;f.push(h)}catch(w){d(w)}});return f}])),e[a].push(f)):m(a,oc(r));return this};this.aHrefSanitizationWhitelist=function(b){return A(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return A(b)?(a.imgSrcSanitizationWhitelist(b), -this):a.imgSrcSanitizationWhitelist()};var n=!0;this.debugInfoEnabled=function(a){return A(a)?(n=a,this):n};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,d,u,q,z,N,ia,O,H){function L(a,b){try{a.addClass(b)}catch(c){}}function W(a,b,c,d,e){a instanceof B||(a=B(a));m(a,function(b,c){b.nodeType==Pa&&b.nodeValue.match(/\S+/)&&(a[c]=B(b).wrap("<span></span>").parent()[0])});var f= -S(a,b,a,c,d,e);W.$$addScopeClass(a);var g=null;return function(b,c,d){qb(b,"scope");d=d||{};var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==wa(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?B(Xb(g,B("<div>").append(a).html())):c?Ra.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",h[k].instance);W.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,e);return d}}function S(a, -b,c,d,e,f){function g(a,c,d,e){var f,k,l,r,n,t,O;if(q)for(O=Array(c.length),r=0;r<h.length;r+=3)f=h[r],O[f]=c[f];else O=c;r=0;for(n=h.length;r<n;)if(k=O[h[r++]],c=h[r++],f=h[r++],c){if(c.scope){if(l=a.$new(),W.$$addScopeInfo(B(k),l),t=c.$$destroyBindings)c.$$destroyBindings=null,l.$on("$destroyed",t)}else l=a;t=c.transcludeOnThisElement?ba(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?ba(a,b):null;c(f,l,k,d,t,c)}else f&&f(a,k.childNodes,w,e)}for(var h=[],k,l,r,n,q,t=0;t<a.length;t++){k=new Z; -l=ca(a[t],[],k,0===t?d:w,e);(f=l.length?D(l,a[t],k,b,c,null,[],[],f):null)&&f.scope&&W.$$addScopeClass(k.$$element);k=f&&f.terminal||!(r=a[t].childNodes)||!r.length?null:S(r,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(t,f,k),n=!0,q=q||f;f=null}return n?g:null}function ba(a,b,c){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g})}}function ca(a,b,c,d,e){var g= -c.$attr,k;switch(a.nodeType){case pa:na(b,ya(wa(a)),"E",d,e);for(var l,r,n,q=a.attributes,t=0,O=q&&q.length;t<O;t++){var K=!1,H=!1;l=q[t];k=l.name;r=T(l.value);l=ya(k);if(n=ja.test(l))k=k.replace(Wc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var S=l.replace(/(Start|End)$/,"");I(S)&&l===S+"Start"&&(K=k,H=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=ya(k.toLowerCase());g[l]=k;if(n||!c.hasOwnProperty(l))c[l]=r,Rc(a,l)&&(c[l]=!0);V(a,b,r,l,n);na(b,l,"A",d,e,K,H)}a= -a.className;C(a)&&(a=a.animVal);if(G(a)&&""!==a)for(;k=h.exec(a);)l=ya(k[2]),na(b,l,"C",d,e)&&(c[l]=T(k[3])),a=a.substr(k.index+k[0].length);break;case Pa:if(11===Wa)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Pa;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);Ka(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=ya(k[1]),na(b,l,"M",d,e)&&(c[l]=T(k[2]))}catch(E){}}b.sort(M);return b}function za(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ga("uterdir", -b,c);a.nodeType==pa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function s(a,b,c){return function(d,e,f,g,h){e=za(e[0],b,c);return a(d,e,f,g,h)}}function D(a,b,d,e,f,g,h,k,r){function n(a,b,c,d){if(a){c&&(a=s(a,c,d));a.require=D.require;a.directiveName=y;if(u===D||D.$$isolateScope)a=$(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=s(b,c,d));b.require=D.require;b.directiveName=y;if(u===D||D.$$isolateScope)b=$(b,{isolateScope:!0});k.push(b)}} -function t(a,b,c,d){var e;if(G(b)){var f=b.match(l);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;e||(d="$"+b+"Controller",e=g?c.inheritedData(d):c.data(d));if(!e&&!f)throw ga("ctreq",b,a);}else if(J(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=t(a,b[g],c,d);return e||null}function O(a,b,c,d,e,f){var g=fa(),h;for(h in d){var k=d[h],l={$scope:k===u||k.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},r=k.controller;"@"==r&&(r=b[k.name]);l=q(r, -l,!0,k.controllerAs);g[k.name]=l;ia||a.data("$"+k.name+"Controller",l.instance)}return g}function K(a,c,e,f,g,l){function r(a,b,c){var d;ab(a)||(c=b,b=a,a=w);ia&&(d=ca);c||(c=ia?N.parent():N);return g(a,b,d,c,za)}var n,q,H,E,ca,z,N;b===e?(f=d,N=d.$$element):(N=B(e),f=new Z(N,d));u&&(E=c.$new(!0));g&&(z=r,z.$$boundTransclude=g);ba&&(ca=O(N,f,z,ba,E,c));u&&(W.$$addScopeInfo(N,E,!0,!(L&&(L===u||L===u.$$originalDirective))),W.$$addScopeClass(N,!0),E.$$isolateBindings=u.$$isolateBindings,Y(c,f,E,E.$$isolateBindings, -u,E));if(ca){var Va=u||S,m;Va&&ca[Va.name]&&(q=Va.$$bindings.bindToController,(H=ca[Va.name])&&H.identifier&&q&&(m=H,l.$$destroyBindings=Y(c,f,H.instance,q,Va)));for(n in ca){H=ca[n];var D=H();D!==H.instance&&(H.instance=D,N.data("$"+n+"Controller",D),H===m&&(l.$$destroyBindings(),l.$$destroyBindings=Y(c,f,D,q,Va)))}}n=0;for(l=h.length;n<l;n++)q=h[n],aa(q,q.isolateScope?E:c,N,f,q.require&&t(q.directiveName,q.require,N,ca),z);var za=c;u&&(u.template||null===u.templateUrl)&&(za=E);a&&a(za,e.childNodes, -w,g);for(n=k.length-1;0<=n;n--)q=k[n],aa(q,q.isolateScope?E:c,N,f,q.require&&t(q.directiveName,q.require,N,ca),z)}r=r||{};for(var H=-Number.MAX_VALUE,S=r.newScopeDirective,ba=r.controllerDirectives,u=r.newIsolateScopeDirective,L=r.templateDirective,z=r.nonTlbTranscludeDirective,N=!1,m=!1,ia=r.hasElementTranscludeDirective,v=d.$$element=B(b),D,y,M,Ka=e,na,I=0,F=a.length;I<F;I++){D=a[I];var P=D.$$start,R=D.$$end;P&&(v=za(b,P,R));M=w;if(H>D.priority)break;if(M=D.scope)D.templateUrl||(C(M)?(Q("new/isolated scope", -u||S,D,v),u=D):Q("new/isolated scope",u,D,v)),S=S||D;y=D.name;!D.templateUrl&&D.controller&&(M=D.controller,ba=ba||fa(),Q("'"+y+"' controller",ba[y],D,v),ba[y]=D);if(M=D.transclude)N=!0,D.$$tlb||(Q("transclusion",z,D,v),z=D),"element"==M?(ia=!0,H=D.priority,M=v,v=d.$$element=B(X.createComment(" "+y+": "+d[y]+" ")),b=v[0],U(f,ua.call(M,0),b),Ka=W(M,e,H,g&&g.name,{nonTlbTranscludeDirective:z})):(M=B(Ub(b)).contents(),v.empty(),Ka=W(M,e));if(D.template)if(m=!0,Q("template",L,D,v),L=D,M=x(D.template)? -D.template(v,d):D.template,M=ha(M),D.replace){g=D;M=Sb.test(M)?Xc(Xb(D.templateNamespace,T(M))):[];b=M[0];if(1!=M.length||b.nodeType!==pa)throw ga("tplrt",y,"");U(f,v,b);F={$attr:{}};M=ca(b,[],F);var Lf=a.splice(I+1,a.length-(I+1));u&&A(M);a=a.concat(M).concat(Lf);Yc(d,F);F=a.length}else v.html(M);if(D.templateUrl)m=!0,Q("template",L,D,v),L=D,D.replace&&(g=D),K=Mf(a.splice(I,a.length-I),v,d,f,N&&Ka,h,k,{controllerDirectives:ba,newScopeDirective:S!==D&&S,newIsolateScopeDirective:u,templateDirective:L, -nonTlbTranscludeDirective:z}),F=a.length;else if(D.compile)try{na=D.compile(v,d,Ka),x(na)?n(null,na,P,R):na&&n(na.pre,na.post,P,R)}catch(V){c(V,xa(v))}D.terminal&&(K.terminal=!0,H=Math.max(H,D.priority))}K.scope=S&&!0===S.scope;K.transcludeOnThisElement=N;K.templateOnThisElement=m;K.transclude=Ka;r.hasElementTranscludeDirective=ia;return K}function A(a){for(var b=0,c=a.length;b<c;b++)a[b]=Nb(a[b],{$$isolateScope:!0})}function na(b,d,f,g,h,k,l){if(d===h)return null;h=null;if(e.hasOwnProperty(d)){var n; -d=a.get(d+"Directive");for(var q=0,t=d.length;q<t;q++)try{n=d[q],(v(g)||g>n.priority)&&-1!=n.restrict.indexOf(f)&&(k&&(n=Nb(n,{$$start:k,$$end:l})),b.push(n),h=n)}catch(H){c(H)}}return h}function I(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=c[d],b.multiElement)return!0;return!1}function Yc(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b,function(b,f){"class"== -f?(L(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function Mf(a,b,c,e,f,g,h,k){var l=[],r,n,q=b[0],t=a.shift(),H=Nb(t,{templateUrl:null,transclude:null,replace:null,$$originalDirective:t}),O=x(t.templateUrl)?t.templateUrl(b,c):t.templateUrl,E=t.templateNamespace;b.empty();d(O).then(function(d){var K,u;d=ha(d);if(t.replace){d=Sb.test(d)?Xc(Xb(E,T(d))): -[];K=d[0];if(1!=d.length||K.nodeType!==pa)throw ga("tplrt",t.name,O);d={$attr:{}};U(e,b,K);var z=ca(K,[],d);C(t.scope)&&A(z);a=z.concat(a);Yc(c,d)}else K=q,b.html(d);a.unshift(H);r=D(a,K,c,f,b,t,g,h,k);m(e,function(a,c){a==K&&(e[c]=b[0])});for(n=S(b[0].childNodes,f);l.length;){d=l.shift();u=l.shift();var N=l.shift(),W=l.shift(),z=b[0];if(!d.$$destroyed){if(u!==q){var za=u.className;k.hasElementTranscludeDirective&&t.replace||(z=Ub(K));U(N,B(u),z);L(B(z),za)}u=r.transcludeOnThisElement?ba(d,r.transclude, -W):W;r(n,d,z,e,u,r)}}l=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(r.transcludeOnThisElement&&(a=ba(b,r.transclude,e)),r(n,b,c,d,a,r)))}}function M(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Q(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ga("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,xa(d));}function Ka(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a= -a.parent();var b=!!a.length;b&&W.$$addBindingClass(a);return function(a,c){var e=c.parent();b||W.$$addBindingClass(e);W.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Xb(a,b){a=F(a||"html");switch(a){case "svg":case "math":var c=X.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function R(a,b){if("srcdoc"==b)return ia.HTML;var c=wa(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b|| -"ngSrc"==b))return ia.RESOURCE_URL}function V(a,c,d,e,f){var h=R(a,e);f=g[e]||f;var l=b(d,!0,h,f);if(l){if("multiple"===e&&"select"===wa(a))throw ga("selmulti",xa(a));c.push({priority:100,compile:function(){return{pre:function(a,c,g){c=g.$$observers||(g.$$observers=fa());if(k.test(e))throw ga("nodomevents");var r=g[e];r!==d&&(l=r&&b(r,!0,h,f),d=r);l&&(g[e]=l(a),(c[e]||(c[e]=[])).$$inter=!0,(g.$$observers&&g.$$observers[e].$$scope||a).$watch(l,function(a,b){"class"===e&&a!=b?g.$updateClass(a,b):g.$set(e, -a)}))}}}})}}function U(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=X.createDocumentFragment();a.appendChild(d);B.hasData(d)&&(B(c).data(B(d).data()),ra?(Qb=!0,ra.cleanData([d])):delete B.cache[d[B.expando]]);d=1;for(e=b.length;d<e;d++)f=b[d],B(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function $(a, -b){return P(function(){return a.apply(null,arguments)},a,b)}function aa(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,xa(d))}}function Y(a,c,d,e,f,g){var h;m(e,function(e,g){var k=e.attrName,l=e.optional,r,n,q,K;switch(e.mode){case "@":l||ta.call(c,k)||(d[g]=c[k]=void 0);c.$observe(k,function(a){G(a)&&(d[g]=a)});c.$$observers[k].$$scope=a;G(c[k])&&(d[g]=b(c[k])(a));break;case "=":if(!ta.call(c,k)){if(l)break;c[k]=void 0}if(l&&!c[k])break;n=u(c[k]);K=n.literal?ka:function(a,b){return a===b||a!==a&&b!== -b};q=n.assign||function(){r=d[g]=n(a);throw ga("nonassign",c[k],f.name);};r=d[g]=n(a);l=function(b){K(b,d[g])||(K(b,r)?q(a,b=d[g]):d[g]=b);return r=b};l.$stateful=!0;l=e.collection?a.$watchCollection(c[k],l):a.$watch(u(c[k],l),null,n.literal);h=h||[];h.push(l);break;case "&":n=c.hasOwnProperty(k)?u(c[k]):y;if(n===y&&l)break;d[g]=function(b){return n(a,b)}}});e=h?function(){for(var a=0,b=h.length;a<b;++a)h[a]()}:y;return g&&e!==y?(g.$on("$destroy",e),y):e}var Z=function(a,b){if(b){var c=Object.keys(b), -d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};Z.prototype={$normalize:ya,$addClass:function(a){a&&0<a.length&&O.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&O.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Zc(a,b);c&&c.length&&O.addClass(this.$$element,c);(c=Zc(b,a))&&c.length&&O.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=Rc(this.$$element[0],a),g=$c[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]= -b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=Ac(a,"-"));f=wa(this.$$element);if("a"===f&&"href"===a||"img"===f&&"src"===a)this[a]=b=H(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=T(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<k;l++)var r=2*l,f=f+H(T(g[r]),!0),f=f+(" "+T(g[r+1]));g=T(g[2*l]).split(/\s/);f+=H(T(g[0]),!0);2===g.length&&(f+=" "+T(g[1]));this[a]=b=f}!1!==d&&(null===b||v(b)?this.$$element.removeAttr(e): -this.$$element.attr(e,b));(a=this.$$observers)&&m(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=fa()),e=d[a]||(d[a]=[]);e.push(b);z.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||v(c[a])||b(c[a])});return function(){cb(e,b)}}};var da=b.startSymbol(),ea=b.endSymbol(),ha="{{"==da||"}}"==ea?$a:function(a){return a.replace(/\{\{/g,da).replace(/}}/g,ea)},ja=/^ngAttr[A-Z]/;W.$$addBindingInfo=n?function(a,b){var c=a.data("$binding")|| -[];J(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:y;W.$$addBindingClass=n?function(a){L(a,"ng-binding")}:y;W.$$addScopeInfo=n?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:y;W.$$addScopeClass=n?function(a,b){L(a,b?"ng-isolate-scope":"ng-scope")}:y;return W}]}function ya(b){return gb(b.replace(Wc,""))}function Zc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var h=d[f],g=0;g<e.length;g++)if(h==e[g])continue a;c+=(0<c.length? -" ":"")+h}return c}function Xc(b){b=B(b);var a=b.length;if(1>=a)return b;for(;a--;)8===b[a].nodeType&&Nf.call(b,a,1);return b}function Xe(){var b={},a=!1;this.register=function(a,d){Ta(a,"controller");C(a)?P(b,a):b[a]=d};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(c,d){function e(a,b,c,d){if(!a||!C(a.$scope))throw I("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,h,g,l){var k,n,p;g=!0===g;l&&G(l)&&(p=l);if(G(f)){l=f.match(Vc);if(!l)throw Of("ctrlfmt",f); -n=l[1];p=p||l[3];f=b.hasOwnProperty(n)?b[n]:Cc(h.$scope,n,!0)||(a?Cc(d,n,!0):w);Sa(f,n,!0)}if(g)return g=(J(f)?f[f.length-1]:f).prototype,k=Object.create(g||null),p&&e(h,p,k,n||f.name),P(function(){var a=c.invoke(f,k,h,n);a!==k&&(C(a)||x(a))&&(k=a,p&&e(h,p,k,n||f.name));return k},{instance:k,identifier:p});k=c.instantiate(f,h,n);p&&e(h,p,k,n||f.name);return k}}]}function Ye(){this.$get=["$window",function(b){return B(b.document)}]}function Ze(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b, -arguments)}}]}function Yb(b){return C(b)?ea(b)?b.toISOString():eb(b):b}function df(){this.$get=function(){return function(b){if(!b)return"";var a=[];nc(b,function(b,d){null===b||v(b)||(J(b)?m(b,function(b,c){a.push(la(d)+"="+la(Yb(b)))}):a.push(la(d)+"="+la(Yb(b))))});return a.join("&")}}}function ef(){this.$get=function(){return function(b){function a(b,e,f){null===b||v(b)||(J(b)?m(b,function(b,c){a(b,e+"["+(C(b)?c:"")+"]")}):C(b)&&!ea(b)?nc(b,function(b,c){a(b,e+(f?"":"[")+c+(f?"":"]"))}):c.push(la(e)+ -"="+la(Yb(b))))}if(!b)return"";var c=[];a(b,"",!0);return c.join("&")}}}function Zb(b,a){if(G(b)){var c=b.replace(Pf,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(ad))||(d=(d=c.match(Qf))&&Rf[d[0]].test(c));d&&(b=vc(c))}}return b}function bd(b){var a=fa(),c;G(b)?m(b.split("\n"),function(b){c=b.indexOf(":");var e=F(T(b.substr(0,c)));b=T(b.substr(c+1));e&&(a[e]=a[e]?a[e]+", "+b:b)}):C(b)&&m(b,function(b,c){var f=F(c),h=T(b);f&&(a[f]=a[f]?a[f]+", "+h:h)});return a}function cd(b){var a; -return function(c){a||(a=bd(b));return c?(c=a[F(c)],void 0===c&&(c=null),c):a}}function dd(b,a,c,d){if(x(d))return d(b,a,c);m(d,function(d){b=d(b,a,c)});return b}function cf(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return C(a)&&"[object File]"!==va.call(a)&&"[object Blob]"!==va.call(a)&&"[object FormData]"!==va.call(a)?eb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ja($b),put:ja($b),patch:ja($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN", -paramSerializer:"$httpParamSerializer"},a=!1;this.useApplyAsync=function(b){return A(b)?(a=!!b,this):a};var c=!0;this.useLegacyPromiseExtensions=function(a){return A(a)?(c=!!a,this):c};var d=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,h,g,l,k){function n(a){function d(a){var b=P({},a);b.data=a.data?dd(a.data,a.headers,a.status,f.transformResponse):a.data;a=a.status;return 200<=a&&300>a?b:l.reject(b)}function e(a,b){var c, -d={};m(a,function(a,e){x(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!da.isObject(a))throw I("$http")("badreq",a);var f=P({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse,paramSerializer:b.paramSerializer},a);f.headers=function(a){var c=b.headers,d=P({},a.headers),f,g,h,c=P({},c.common,c[F(a.method)]);a:for(f in c){g=F(f);for(h in d)if(F(h)===g)continue a;d[f]=c[f]}return e(d,ja(a))}(a);f.method=sb(f.method);f.paramSerializer=G(f.paramSerializer)?k.get(f.paramSerializer): -f.paramSerializer;var g=[function(a){var c=a.headers,e=dd(a.data,cd(c),w,a.transformRequest);v(e)&&m(c,function(a,b){"content-type"===F(b)&&delete c[b]});v(a.withCredentials)&&!v(b.withCredentials)&&(a.withCredentials=b.withCredentials);return p(a,e).then(d,d)},w],h=l.when(f);for(m(E,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=g.shift();var r=g.shift(),h=h.then(a,r)}c?(h.success=function(a){Sa(a, -"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Sa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=ed("success"),h.error=ed("error"));return h}function p(c,d){function h(b,c,d,e){function f(){k(c,b,d,e)}L&&(200<=b&&300>b?L.put(ba,[b,c,bd(d),e]):L.remove(ba));a?g.$applyAsync(f):(f(),g.$$phase||g.$apply())}function k(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?O.resolve:O.reject)({data:a,status:b,headers:cd(d),config:c,statusText:e})} -function p(a){k(a.data,a.status,ja(a.headers()),a.statusText)}function E(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var O=l.defer(),H=O.promise,L,m,S=c.headers,ba=r(c.url,c.paramSerializer(c.params));n.pendingRequests.push(c);H.then(E,E);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(L=C(c.cache)?c.cache:C(b.cache)?b.cache:t);L&&(m=L.get(ba),A(m)?m&&x(m.then)?m.then(p,p):J(m)?k(m[1],m[0],ja(m[2]),m[3]):k(m,200,{},"OK"):L.put(ba,H));v(m)&&((m= -fd(c.url)?f()[c.xsrfCookieName||b.xsrfCookieName]:w)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=m),e(c.method,ba,d,h,S,c.timeout,c.withCredentials,c.responseType));return H}function r(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var t=h("$http");b.paramSerializer=G(b.paramSerializer)?k.get(b.paramSerializer):b.paramSerializer;var E=[];m(d,function(a){E.unshift(G(a)?k.get(a):k.invoke(a))});n.pendingRequests=[];(function(a){m(arguments,function(a){n[a]=function(b,c){return n(P({},c||{}, -{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){n[a]=function(b,c,d){return n(P({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");n.defaults=b;return n}]}function gf(){this.$get=function(){return function(){return new Q.XMLHttpRequest}}}function ff(){this.$get=["$browser","$window","$document","$xhrFactory",function(b,a,c,d){return Sf(b,d,b.defer,a.angular.callbacks,c[0])}]}function Sf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"), -n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);f=null;var h=-1,t="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),t=a.type,h="error"===a.type?404:200);c&&c(h,t)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,g,l,k,n,p,r,t){function E(){q&&q();z&&z.abort()}function K(a,d,e,f,g){A(s)&&c.cancel(s);q=z=null;a(d, -e,f,g);b.$$completeOutstandingRequest(y)}b.$$incOutstandingRequestCount();g=g||b.url();if("jsonp"==F(e)){var u="_"+(d.counter++).toString(36);d[u]=function(a){d[u].data=a;d[u].called=!0};var q=f(g.replace("JSON_CALLBACK","angular.callbacks."+u),u,function(a,b){K(k,a,d[u].data,"",b);d[u]=y})}else{var z=a(e,g);z.open(e,g,!0);m(n,function(a,b){A(a)&&z.setRequestHeader(b,a)});z.onload=function(){var a=z.statusText||"",b="response"in z?z.response:z.responseText,c=1223===z.status?204:z.status;0===c&&(c= -b?200:"file"==Aa(g).protocol?404:0);K(k,c,b,z.getAllResponseHeaders(),a)};e=function(){K(k,-1,null,null,"")};z.onerror=e;z.onabort=e;r&&(z.withCredentials=!0);if(t)try{z.responseType=t}catch(N){if("json"!==t)throw N;}z.send(v(l)?null:l)}if(0<p)var s=c(E,p);else p&&x(p.then)&&p.then(E)}}function af(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(a){return"\\\\\\"+ -a}function h(c){return c.replace(n,b).replace(p,a)}function g(f,g,n,p){function u(a){try{var b=a;a=n?e.getTrusted(n,b):e.valueOf(b);var c;if(p&&!A(a))c=a;else if(null==a)c="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=eb(a)}c=a}return c}catch(g){d(La.interr(f,g))}}p=!!p;for(var q,m,N=0,s=[],O=[],H=f.length,L=[],W=[];N<H;)if(-1!=(q=f.indexOf(b,N))&&-1!=(m=f.indexOf(a,q+l)))N!==q&&L.push(h(f.substring(N,q))),N=f.substring(q+l,m),s.push(N),O.push(c(N,u)),N=m+k,W.push(L.length), -L.push("");else{N!==H&&L.push(h(f.substring(N)));break}n&&1<L.length&&La.throwNoconcat(f);if(!g||s.length){var S=function(a){for(var b=0,c=s.length;b<c;b++){if(p&&v(a[b]))return;L[W[b]]=a[b]}return L.join("")};return P(function(a){var b=0,c=s.length,e=Array(c);try{for(;b<c;b++)e[b]=O[b](a);return S(e)}catch(g){d(La.interr(f,g))}},{exp:f,expressions:s,$$watchDelegate:function(a,b){var c;return a.$watchGroup(O,function(d,e){var f=S(d);x(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=b.length,k=a.length, -n=new RegExp(b.replace(/./g,f),"g"),p=new RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol=function(){return a};return g}]}function bf(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,g,l,k){var n=4<arguments.length,p=n?ua.call(arguments,4):[],r=a.setInterval,t=a.clearInterval,E=0,K=A(k)&&!k,u=(K?d:c).defer(),q=u.promise;l=A(l)?l:0;q.then(null,null,n?function(){e.apply(null,p)}:e);q.$$intervalId=r(function(){u.notify(E++);0<l&&E>=l&&(u.resolve(E), -t(q.$$intervalId),delete f[q.$$intervalId]);K||b.$apply()},g);f[q.$$intervalId]=u;return q}var f={};e.cancel=function(b){return b&&b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete f[b.$$intervalId],!0):!1};return e}]}function ac(b){b=b.split("/");for(var a=b.length;a--;)b[a]=ob(b[a]);return b.join("/")}function gd(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=Y(c.port)||Tf[c.protocol]||null}function hd(b,a){var c="/"!==b.charAt(0); -c&&(b="/"+b);var d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=yc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function sa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ja(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Cb(b){return b.replace(/(#.+)|#$/,"$1")}function bc(b,a,c){this.$$html5=!0;c=c||"";gd(b,this);this.$$parse=function(b){var c=sa(a, -b);if(!G(c))throw Db("ipthprfx",b,a);hd(c,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var b=Pb(this.$$search),c=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=ac(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=a+this.$$url.substr(1)};this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,h;A(f=sa(b,d))?(h=f,h=A(f=sa(c,f))?a+(sa("/",f)||f):b+h):A(f=sa(a,d))?h=a+f:a==d+"/"&&(h=a);h&&this.$$parse(h);return!!h}}function cc(b,a,c){gd(b,this); -this.$$parse=function(d){var e=sa(b,d)||sa(a,d),f;v(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",v(e)&&(b=d,this.replace())):(f=sa(c,e),v(f)&&(f=e));hd(f,this);d=this.$$path;var e=b,h=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));h.exec(f)||(d=(f=h.exec(d))?f[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var a=Pb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+(this.$$url?c+this.$$url:"")};this.$$parseLinkUrl= -function(a,c){return Ja(b)==Ja(a)?(this.$$parse(a),!0):!1}}function id(b,a,c){this.$$html5=!0;cc.apply(this,arguments);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,h;b==Ja(d)?f=d:(h=sa(a,d))?f=b+c+h:a===d+"/"&&(f=a);f&&this.$$parse(f);return!!f};this.$$compose=function(){var a=Pb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+c+this.$$url}}function Eb(b){return function(){return this[b]}}function jd(b, -a){return function(c){if(v(c))return this[b];this[b]=a(c);this.$$compose();return this}}function hf(){var b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){return A(a)?(b=a,this):b};this.html5Mode=function(b){return bb(b)?(a.enabled=b,this):C(b)?(bb(b.enabled)&&(a.enabled=b.enabled),bb(b.requireBase)&&(a.requireBase=b.requireBase),bb(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c, -d,e,f,h){function g(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var p=d.url(),r;if(a.enabled){if(!n&&a.requireBase)throw Db("nobase");r=p.substring(0,p.indexOf("/",p.indexOf("//")+2))+(n||"/");n=e.history?bc:id}else r=Ja(p),n=cc;var t=r.substr(0,Ja(r).lastIndexOf("/")+1);k=new n(r,t,"#"+b);k.$$parseLinkUrl(p,p);k.$$state=d.state(); -var E=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKey&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var e=B(b.target);"a"!==wa(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var g=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");C(g)&&"[object SVGAnimatedString]"===g.toString()&&(g=Aa(g.animVal).href);E.test(g)||!g||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(g,l)||(b.preventDefault(),k.absUrl()!=d.url()&&(c.$apply(),h.angular["ff-684208-preventDefault"]= -!0))}});Cb(k.absUrl())!=Cb(p)&&d.url(k.absUrl(),!0);var K=!0;d.onUrlChange(function(a,b){v(sa(t,a))?h.location.href=a:(c.$evalAsync(function(){var d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,g(d,!1,e)):(K=!1,l(d,e)))}),c.$$phase||c.$digest())});c.$watch(function(){var a=Cb(d.url()),b=Cb(k.absUrl()),f=d.state(),h=k.$$replace,r=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(K||r)K=!1, -c.$evalAsync(function(){var b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPrevented;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state=f):(r&&g(b,h,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function jf(){var b=!0,a=this;this.debugEnabled=function(a){return A(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a= -a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||y;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];m(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function Xa(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"=== -b||"__proto__"===b)throw Z("isecfld",a);return b}function kd(b,a){b+="";if(!G(b))throw Z("iseccst",a);return b}function Ba(b,a){if(b){if(b.constructor===b)throw Z("isecfn",a);if(b.window===b)throw Z("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw Z("isecdom",a);if(b===Object)throw Z("isecobj",a);}return b}function ld(b,a){if(b){if(b.constructor===b)throw Z("isecfn",a);if(b===Uf||b===Vf||b===Wf)throw Z("isecff",a);}}function md(b,a){if(b&&(b===(0).constructor||b===(!1).constructor|| -b==="".constructor||b==={}.constructor||b===[].constructor||b===Function.constructor))throw Z("isecaf",a);}function Xf(b,a){return"undefined"!==typeof b?b:a}function nd(b,a){return"undefined"===typeof b?a:"undefined"===typeof a?b:b+a}function U(b,a){var c,d;switch(b.type){case s.Program:c=!0;m(b.body,function(b){U(b.expression,a);c=c&&b.expression.constant});b.constant=c;break;case s.Literal:b.constant=!0;b.toWatch=[];break;case s.UnaryExpression:U(b.argument,a);b.constant=b.argument.constant;b.toWatch= -b.argument.toWatch;break;case s.BinaryExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.left.toWatch.concat(b.right.toWatch);break;case s.LogicalExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.constant?[]:[b];break;case s.ConditionalExpression:U(b.test,a);U(b.alternate,a);U(b.consequent,a);b.constant=b.test.constant&&b.alternate.constant&&b.consequent.constant;b.toWatch=b.constant?[]:[b];break;case s.Identifier:b.constant= -!1;b.toWatch=[b];break;case s.MemberExpression:U(b.object,a);b.computed&&U(b.property,a);b.constant=b.object.constant&&(!b.computed||b.property.constant);b.toWatch=[b];break;case s.CallExpression:c=b.filter?!a(b.callee.name).$stateful:!1;d=[];m(b.arguments,function(b){U(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=b.filter&&!a(b.callee.name).$stateful?d:[b];break;case s.AssignmentExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant; -b.toWatch=[b];break;case s.ArrayExpression:c=!0;d=[];m(b.elements,function(b){U(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=d;break;case s.ObjectExpression:c=!0;d=[];m(b.properties,function(b){U(b.value,a);c=c&&b.value.constant;b.value.constant||d.push.apply(d,b.value.toWatch)});b.constant=c;b.toWatch=d;break;case s.ThisExpression:b.constant=!1,b.toWatch=[]}}function od(b){if(1==b.length){b=b[0].expression;var a=b.toWatch;return 1!==a.length?a:a[0]!==b?a:w}} -function pd(b){return b.type===s.Identifier||b.type===s.MemberExpression}function qd(b){if(1===b.body.length&&pd(b.body[0].expression))return{type:s.AssignmentExpression,left:b.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function rd(b){return 0===b.body.length||1===b.body.length&&(b.body[0].expression.type===s.Literal||b.body[0].expression.type===s.ArrayExpression||b.body[0].expression.type===s.ObjectExpression)}function sd(b,a){this.astBuilder=b;this.$filter=a}function td(b, -a){this.astBuilder=b;this.$filter=a}function Fb(b){return"constructor"==b}function dc(b){return x(b.valueOf)?b.valueOf():Yf.call(b)}function kf(){var b=fa(),a=fa();this.$get=["$filter",function(c){function d(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=dc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function e(a,b,c,e,f){var g=e.inputs,h;if(1===g.length){var k=d,g=g[0];return a.$watch(function(a){var b=g(a);d(b,k)||(h=e(a,w,w,[b]),k=b&&dc(b));return h},b,c,f)}for(var l=[],n=[],p=0, -m=g.length;p<m;p++)l[p]=d,n[p]=null;return a.$watch(function(a){for(var b=!1,c=0,f=g.length;c<f;c++){var k=g[c](a);if(b||(b=!d(k,l[c])))n[c]=k,l[c]=k&&dc(k)}b&&(h=e(a,w,w,n));return h},b,c,f)}function f(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;x(b)&&b.apply(this,arguments);A(a)&&d.$$postDigest(function(){A(f)&&e()})},c)}function h(a,b,c,d){function e(a){var b=!0;m(a,function(a){A(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a, -c,d){g=a;x(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function g(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){x(b)&&b.apply(this,arguments);e()},c)}function l(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==h&&c!==f?function(c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return A(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==e?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=e,c.inputs= -a.inputs?a.inputs:[a]);return c}var k=Fa().noUnsafeEval,n={csp:k,expensiveChecks:!1},p={csp:k,expensiveChecks:!0};return function(d,k,E){var m,u,q;switch(typeof d){case "string":q=d=d.trim();var s=E?a:b;m=s[q];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(u=!0,d=d.substring(2)),E=E?p:n,m=new ec(E),m=(new fc(m,c,E)).parse(d),m.constant?m.$$watchDelegate=g:u?m.$$watchDelegate=m.literal?h:f:m.inputs&&(m.$$watchDelegate=e),s[q]=m);return l(m,k);case "function":return l(d,k);default:return y}}}]}function mf(){this.$get= -["$rootScope","$exceptionHandler",function(b,a){return ud(function(a){b.$evalAsync(a)},a)}]}function nf(){this.$get=["$browser","$exceptionHandler",function(b,a){return ud(function(a){b.defer(a)},a)}]}function ud(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending; -c.processScheduled=!1;c.pending=w;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{x(b)?d.resolve(b(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),a(h)}}}))}function h(){this.promise=new d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var g=I("$q",TypeError);P(d.prototype,{then:function(a,b,c){if(v(a)&&v(b)&&v(c))return this;var d=new h;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d, -a,b,c]);0<this.$$state.status&&f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}});P(h.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(g("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(C(b)||x(b))d=b&&b.then;x(d)?(this.promise.$$state.status=-1,d.call(b,e[0],e[1], -this.notify)):(this.promise.$$state.value=b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),a(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b,e,f=0,g=d.length;f<g;f++){e=d[f][0];b=d[f][3];try{e.notify(x(b)?b(c):c)}catch(h){a(h)}}})}}); -var l=function(a,b){var c=new h;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{x(c)&&(d=c())}catch(e){return l(e,!1)}return d&&x(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new h;e.resolve(a);return e.promise.then(b,c,d)},p=function t(a){if(!x(a))throw g("norslvr",a);if(!(this instanceof t))return new t(a);var b=new h;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};p.defer=function(){return new h}; -p.reject=function(a){var b=new h;b.reject(a);return b.promise};p.when=n;p.resolve=n;p.all=function(a){var b=new h,c=0,d=J(a)?[]:{};m(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return p}function wf(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFrame, -e=!!c,f=e?function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function lf(){function b(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=null}b.prototype=a;return b}var a=10,c=I("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length&&(a=b);return a};this.$get= -["$injector","$exceptionHandler","$parse","$browser",function(f,h,g,l){function k(a){a.currentScope.$$destroyed=!0}function n(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function p(a){if(q.$$phase)throw c("inprog",q.$$phase);q.$$phase=a}function r(a,b){do a.$$watchersCount+=b;while(a= -a.$parent)}function t(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function E(){}function s(){for(;w.length;)try{w.shift()()}catch(a){h(a)}e=null}function u(){null===e&&(e=l.defer(function(){q.$apply(s)}))}n.prototype={constructor:n,$new:function(a,c){var d;c=c||this;a?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling= -d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,c,e){var f=g(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,c,f,a);var h=this,k=h.$$watchers,l={fn:b,last:E,get:f,exp:e||a,eq:!!c};d=null;x(b)||(l.fn=y);k||(k=h.$$watchers=[]);k.unshift(l);r(this,1);return function(){0<=cb(k,l)&&r(h,-1);d=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0; -if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});m(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!v(e)){if(C(e))if(Da(e))for(f!==p&&(f=p,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=a),b=0;b<a;b++)h=f[b], -g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==r&&(f=r={},t=0,l++);a=0;for(b in e)ta.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(t++,f[b]=g,l++));if(t>a)for(b in l++,f)ta.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1<b.length,l=0,n=g(a,c),p=[],r={},q=!0,t=0;return this.$watch(n,function(){q?(q=!1,b(e,e,d)):b(e,h,d);if(k)if(C(e))if(Da(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h= -{},e)ta.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var b,f,g,k,n,r,t=a,m,u=[],D,v;p("$digest");l.$$checkUrlChange();this===q&&null!==e&&(l.defer.cancel(e),s());d=null;do{r=!1;for(m=this;z.length;){try{v=z.shift(),v.scope.$eval(v.expression,v.locals)}catch(w){h(w)}d=null}a:do{if(k=m.$$watchers)for(n=k.length;n--;)try{if(b=k[n])if((f=b.get(m))!==(g=b.last)&&!(b.eq?ka(f,g):"number"===typeof f&&"number"===typeof g&&isNaN(f)&&isNaN(g)))r=!0,d=b,b.last=b.eq?ha(f,null):f,b.fn(f,g===E?f:g,m),5> -t&&(D=4-t,u[D]||(u[D]=[]),u[D].push({msg:x(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:g}));else if(b===d){r=!1;break a}}catch(y){h(y)}if(!(k=m.$$watchersCount&&m.$$childHead||m!==this&&m.$$nextSibling))for(;m!==this&&!(k=m.$$nextSibling);)m=m.$parent}while(m=k);if((r||z.length)&&!t--)throw q.$$phase=null,c("infdig",a,u);}while(r||z.length);for(q.$$phase=null;N.length;)try{N.shift()()}catch(A){h(A)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy"); -this.$$destroyed=!0;this===q&&l.$$applicationDestroyed();r(this,-this.$$watchersCount);for(var b in this.$$listenerCount)t(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=y;this.$on= -this.$watch=this.$watchGroup=function(){return y};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=this.$$watchers=null}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){q.$$phase||z.length||l.defer(function(){z.length&&q.$digest()});z.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){N.push(a)},$apply:function(a){try{p("$apply");try{return this.$eval(a)}finally{q.$$phase=null}}catch(b){h(b)}finally{try{q.$digest()}catch(c){throw h(c), -c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&w.push(b);u()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,t(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,g={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){g.defaultPrevented=!0},defaultPrevented:!1}, -k=db([g],arguments,1),l,n;do{d=e.$$listeners[a]||c;g.currentScope=e;l=0;for(n=d.length;l<n;l++)if(d[l])try{d[l].apply(null,k)}catch(p){h(p)}else d.splice(l,1),l--,n--;if(f)return g.currentScope=null,g;e=e.$parent}while(e);g.currentScope=null;return g},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var f=db([e],arguments,1),g,k;c=d;){e.currentScope=c;d=c.$$listeners[a]|| -[];g=0;for(k=d.length;g<k;g++)if(d[g])try{d[g].apply(null,f)}catch(l){h(l)}else d.splice(g,1),g--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var q=new n,z=q.$$asyncQueue=[],N=q.$$postDigestQueue=[],w=q.$$applyAsyncQueue=[];return q}]}function ge(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return A(a)? -(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return A(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;f=Aa(c).href;return""===f||f.match(e)?c:"unsafe:"+f}}}function Zf(b){if("self"===b)return b;if(G(b)){if(-1<b.indexOf("***"))throw Ca("iwcard",b);b=vd(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(Oa(b))return new RegExp("^"+b.source+"$");throw Ca("imatcher");}function wd(b){var a=[];A(b)&&m(b,function(b){a.push(Zf(b))}); -return a}function pf(){this.SCE_CONTEXTS=oa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=wd(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=wd(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?fd(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()}; -return b}var f=function(a){throw Ca("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var h=e(),g={};g[oa.HTML]=e(h);g[oa.CSS]=e(h);g[oa.URL]=e(h);g[oa.JS]=e(h);g[oa.RESOURCE_URL]=e(g[oa.URL]);return{trustAs:function(a,b){var c=g.hasOwnProperty(a)?g[a]:null;if(!c)throw Ca("icontext",a,b);if(null===b||v(b)||""===b)return b;if("string"!==typeof b)throw Ca("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||v(e)||""===e)return e;var h=g.hasOwnProperty(c)?g[c]:null;if(h&&e instanceof -h)return e.$$unwrapTrustedValue();if(c===oa.RESOURCE_URL){var h=Aa(e.toString()),p,r,t=!1;p=0;for(r=b.length;p<r;p++)if(d(b[p],h)){t=!0;break}if(t)for(p=0,r=a.length;p<r;p++)if(d(a[p],h)){t=!1;break}if(t)return e;throw Ca("insecurl",e.toString());}if(c===oa.HTML)return f(e);throw Ca("unsafe");},valueOf:function(a){return a instanceof h?a.$$unwrapTrustedValue():a}}}]}function of(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&& -8>Wa)throw Ca("iequirks");var d=ja(oa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf=c.valueOf;b||(d.trustAs=d.getTrusted=function(a,b){return b},d.valueOf=$a);d.parseAs=function(b,c){var e=a(c);return e.literal&&e.constant?e:a(c,function(a){return d.getTrusted(b,a)})};var e=d.parseAs,f=d.getTrusted,h=d.trustAs;m(oa,function(a,b){var c=F(b);d[gb("parse_as_"+c)]=function(b){return e(a,b)};d[gb("get_trusted_"+c)]=function(b){return f(a,b)};d[gb("trust_as_"+ -c)]=function(b){return h(a,b)}});return d}]}function qf(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(F((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},h,g=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var p in l)if(k=g.exec(p)){h=k[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||h+"Transition"in l);n=!!("animation"in l||h+"Animation"in -l);!d||k&&n||(k=G(l.webkitTransition),n=G(l.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"===a&&11>=Wa)return!1;if(v(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Fa(),vendorPrefix:h,transitions:k,animations:n,android:d}}]}function sf(){this.$get=["$templateCache","$http","$q","$sce",function(b,a,c,d){function e(f,h){e.totalPendingRequests++;G(f)&&b.get(f)||(f=d.getTrustedResourceUrl(f));var g=a.defaults&&a.defaults.transformResponse; -J(g)?g=g.filter(function(a){return a!==Zb}):g===Zb&&(g=null);return a.get(f,{cache:b,transformResponse:g})["finally"](function(){e.totalPendingRequests--}).then(function(a){b.put(f,a.data);return a.data},function(a){if(!h)throw ga("tpload",f,a.status,a.statusText);return c.reject(a)})}e.totalPendingRequests=0;return e}]}function tf(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var h=[];m(a,function(a){var d= -da.element(a).data("$binding");d&&m(d,function(d){c?(new RegExp("(^|\\s)"+vd(b)+"(\\s|\\||$)")).test(d)&&h.push(a):-1!=d.indexOf(b)&&h.push(a)})});return h},findModels:function(a,b,c){for(var h=["ng-","data-ng-","ng\\:"],g=0;g<h.length;++g){var l=a.querySelectorAll("["+h[g]+"model"+(c?"=":"*=")+'"'+b+'"]');if(l.length)return l}},getLocation:function(){return c.url()},setLocation:function(a){a!==c.url()&&(c.url(a),b.$digest())},whenStable:function(b){a.notifyWhenNoOutstandingRequests(b)}}}]}function uf(){this.$get= -["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){x(f)||(k=l,l=f,f=y);var n=ua.call(arguments,3),p=A(k)&&!k,r=(p?d:c).defer(),t=r.promise,m;m=a.defer(function(){try{r.resolve(f.apply(null,n))}catch(a){r.reject(a),e(a)}finally{delete h[t.$$timeoutId]}p||b.$apply()},l);t.$$timeoutId=m;h[m]=r;return t}var h={};f.cancel=function(b){return b&&b.$$timeoutId in h?(h[b.$$timeoutId].reject("canceled"),delete h[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return f}]} -function Aa(b){Wa&&($.setAttribute("href",b),b=$.href);$.setAttribute("href",b);return{href:$.href,protocol:$.protocol?$.protocol.replace(/:$/,""):"",host:$.host,search:$.search?$.search.replace(/^\?/,""):"",hash:$.hash?$.hash.replace(/^#/,""):"",hostname:$.hostname,port:$.port,pathname:"/"===$.pathname.charAt(0)?$.pathname:"/"+$.pathname}}function fd(b){b=G(b)?Aa(b):b;return b.protocol===xd.protocol&&b.host===xd.host}function vf(){this.$get=qa(Q)}function yd(b){function a(a){try{return decodeURIComponent(a)}catch(b){return a}} -var c=b[0]||{},d={},e="";return function(){var b,h,g,l,k;b=c.cookie||"";if(b!==e)for(e=b,b=e.split("; "),d={},g=0;g<b.length;g++)h=b[g],l=h.indexOf("="),0<l&&(k=a(h.substring(0,l)),v(d[k])&&(d[k]=a(h.substring(l+1))));return d}}function zf(){this.$get=yd}function Kc(b){function a(c,d){if(C(c)){var e={};m(c,function(b,c){e[c]=a(c,b)});return e}return b.factory(c+"Filter",d)}this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];a("currency",zd);a("date",Ad); -a("filter",$f);a("json",ag);a("limitTo",bg);a("lowercase",cg);a("number",Bd);a("orderBy",Cd);a("uppercase",dg)}function $f(){return function(b,a,c){if(!Da(b)){if(null==b)return b;throw I("filter")("notarray",b);}var d;switch(gc(a)){case "function":break;case "boolean":case "null":case "number":case "string":d=!0;case "object":a=eg(a,c,d);break;default:return b}return Array.prototype.filter.call(b,a)}}function eg(b,a,c){var d=C(b)&&"$"in b;!0===a?a=ka:x(a)||(a=function(a,b){if(v(a))return!1;if(null=== -a||null===b)return a===b;if(C(b)||C(a)&&!qc(a))return!1;a=F(""+a);b=F(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!C(e)?Ma(e,b.$,a,!1):Ma(e,b,a,c)}}function Ma(b,a,c,d,e){var f=gc(b),h=gc(a);if("string"===h&&"!"===a.charAt(0))return!Ma(b,a.substring(1),c,d);if(J(b))return b.some(function(b){return Ma(b,a,c,d)});switch(f){case "object":var g;if(d){for(g in b)if("$"!==g.charAt(0)&&Ma(b[g],a,c,!0))return!0;return e?!1:Ma(b,a,c,!1)}if("object"===h){for(g in a)if(e=a[g],!x(e)&&!v(e)&& -(f="$"===g,!Ma(f?b:b[g],e,c,f,f)))return!1;return!0}return c(b,a);case "function":return!1;default:return c(b,a)}}function gc(b){return null===b?"null":typeof b}function zd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){v(d)&&(d=a.CURRENCY_SYM);v(e)&&(e=a.PATTERNS[1].maxFrac);return null==b?b:Dd(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,e).replace(/\u00A4/g,d)}}function Bd(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==b?b:Dd(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Dd(b, -a,c,d,e){if(C(b))return"";var f=0>b;b=Math.abs(b);var h=Infinity===b;if(!h&&!isFinite(b))return"";var g=b+"",l="",k=!1,n=[];h&&(l="\u221e");if(!h&&-1!==g.indexOf("e")){var p=g.match(/([\d\.]+)e(-?)(\d+)/);p&&"-"==p[2]&&p[3]>e+1?b=0:(l=g,k=!0)}if(h||k)0<e&&1>b&&(l=b.toFixed(e),b=parseFloat(l),l=l.replace(hc,d));else{h=(g.split(hc)[1]||"").length;v(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);var h=(""+b).split(hc),g=h[0],h=h[1]||"",p=0, -r=a.lgSize,t=a.gSize;if(g.length>=r+t)for(p=g.length-r,k=0;k<p;k++)0===(p-k)%t&&0!==k&&(l+=c),l+=g.charAt(k);for(k=p;k<g.length;k++)0===(g.length-k)%r&&0!==k&&(l+=c),l+=g.charAt(k);for(;h.length<e;)h+="0";e&&"0"!==e&&(l+=d+h.substr(0,e))}0===b&&(f=!1);n.push(f?a.negPre:a.posPre,l,f?a.negSuf:a.posSuf);return n.join("")}function Gb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function aa(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0< -c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Gb(e,a,d)}}function Hb(b,a){return function(c,d){var e=c["get"+b](),f=sb(a?"SHORT"+b:b);return d[f][e]}}function Ed(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Fd(b){return function(a){var c=Ed(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Gb(a,b)}}function ic(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function Ad(b){function a(a){var b;if(b= -a.match(c)){a=new Date(0);var f=0,h=0,g=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Y(b[9]+b[10]),h=Y(b[9]+b[11]));g.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));f=Y(b[4]||0)-f;h=Y(b[5]||0)-h;g=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,f,h,g,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e,f){var h="",g=[],l,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;G(c)&&(c= -fg.test(c)?Y(c):a(c));V(c)&&(c=new Date(c));if(!ea(c)||!isFinite(c.getTime()))return c;for(;e;)(k=gg.exec(e))?(g=db(g,k,1),e=g.pop()):(g.push(e),e=null);var n=c.getTimezoneOffset();f&&(n=wc(f,c.getTimezoneOffset()),c=Ob(c,f,!0));m(g,function(a){l=hg[a];h+=l?l(c,b.DATETIME_FORMATS,n):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return h}}function ag(){return function(b,a){v(a)&&(a=2);return eb(b,a)}}function bg(){return function(b,a,c){a=Infinity===Math.abs(Number(a))?Number(a):Y(a);if(isNaN(a))return b; -V(b)&&(b=b.toString());if(!J(b)&&!G(b))return b;c=!c||isNaN(c)?0:Y(c);c=0>c&&c>=-b.length?b.length+c:c;return 0<=a?b.slice(c,c+a):0===c?b.slice(a,b.length):b.slice(Math.max(0,c+a),c)}}function Cd(b){function a(a,c){c=c?-1:1;return a.map(function(a){var d=1,g=$a;if(x(a))g=a;else if(G(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))d="-"==a.charAt(0)?-1:1,a=a.substring(1);if(""!==a&&(g=b(a),g.constant))var l=g(),g=function(a){return a[l]}}return{get:g,descending:d*c}})}function c(a){switch(typeof a){case "number":case "boolean":case "string":return!0; -default:return!1}}return function(b,e,f){if(!Da(b))return b;J(e)||(e=[e]);0===e.length&&(e=["+"]);var h=a(e,f);h.push({get:function(){return{}},descending:f?-1:1});b=Array.prototype.map.call(b,function(a,b){return{value:a,predicateValues:h.map(function(d){var e=d.get(a);d=typeof e;if(null===e)d="string",e="null";else if("string"===d)e=e.toLowerCase();else if("object"===d)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),c(e)))break a;if(qc(e)&&(e=e.toString(),c(e)))break a;e=b}return{value:e,type:d}})}}); -b.sort(function(a,b){for(var c=0,d=0,e=h.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],t=0;c.type===f.type?c.value!==f.value&&(t=c.value<f.value?-1:1):t=c.type<f.type?-1:1;if(c=t*h[d].descending)break}return c});return b=b.map(function(a){return a.value})}}function Na(b){x(b)&&(b={link:b});b.restrict=b.restrict||"AC";return qa(b)}function Gd(b,a,c,d,e){var f=this,h=[];f.$error={};f.$$success={};f.$pending=w;f.$name=e(a.name||a.ngForm||"")(c);f.$dirty=!1;f.$pristine=!0;f.$valid= -!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Ib;f.$rollbackViewValue=function(){m(h,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){m(h,function(a){a.$commitViewValue()})};f.$addControl=function(a){Ta(a.$name,"input");h.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];m(f.$pending,function(b,c){f.$setValidity(c,null,a)}); -m(f.$error,function(b,c){f.$setValidity(c,null,a)});m(f.$$success,function(b,c){f.$setValidity(c,null,a)});cb(h,a);a.$$parentForm=Ib};Hd({ctrl:this,$element:b,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(cb(d,c),0===d.length&&delete a[b])},$animate:d});f.$setDirty=function(){d.removeClass(b,Ya);d.addClass(b,Jb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};f.$setPristine=function(){d.setClass(b,Ya,Jb+" ng-submitted");f.$dirty= -!1;f.$pristine=!0;f.$submitted=!1;m(h,function(a){a.$setPristine()})};f.$setUntouched=function(){m(h,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function jc(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function jb(b,a,c,d,e,f){var h=F(a[0].type);if(!e.android){var g=!1;a.on("compositionstart",function(a){g=!0});a.on("compositionend",function(){g=!1;l()})}var l=function(b){k&&(f.defer.cancel(k), -k=null);if(!g){var e=a.val();b=b&&b.type;"password"===h||c.ngTrim&&"false"===c.ngTrim||(e=T(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};if(e.hasEvent("input"))a.on("input",l);else{var k,n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){var b=d.$isEmpty(d.$viewValue)? -"":d.$viewValue;a.val()!==b&&a.val(b)}}function Kb(b,a){return function(c,d){var e,f;if(ea(c))return c;if(G(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));if(ig.test(c))return new Date(c);b.lastIndex=0;if(e=b.exec(c))return e.shift(),f=d?{yyyy:d.getFullYear(),MM:d.getMonth()+1,dd:d.getDate(),HH:d.getHours(),mm:d.getMinutes(),ss:d.getSeconds(),sss:d.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},m(e,function(b,c){c<a.length&&(f[a[c]]=+b)}),new Date(f.yyyy, -f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(b,a,c,d){return function(e,f,h,g,l,k,n){function p(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function r(a){return A(a)&&!ea(a)?c(a)||w:a}Id(e,f,h,g);jb(e,f,h,g,l,k);var t=g&&g.$options&&g.$options.timezone,m;g.$$parserName=b;g.$parsers.push(function(b){return g.$isEmpty(b)?null:a.test(b)?(b=c(b,m),t&&(b=Ob(b,t)),b):w});g.$formatters.push(function(a){if(a&&!ea(a))throw lb("datefmt",a);if(p(a))return(m=a)&&t&&(m=Ob(m,t,!0)), -n("date")(a,d,t);m=null;return""});if(A(h.min)||h.ngMin){var s;g.$validators.min=function(a){return!p(a)||v(s)||c(a)>=s};h.$observe("min",function(a){s=r(a);g.$validate()})}if(A(h.max)||h.ngMax){var u;g.$validators.max=function(a){return!p(a)||v(u)||c(a)<=u};h.$observe("max",function(a){u=r(a);g.$validate()})}}}function Id(b,a,c,d){(d.$$hasNativeValidators=C(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?w:b})}function Jd(b,a,c,d,e){if(A(d)){b= -b(d);if(!b.constant)throw lb("constexpr",c,d);return b(a)}return e}function kc(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){var b=[];return J(a)?(m(a,function(a){b=b.concat(e(a))}),b):G(a)?a.split(" "):C(a)?(m(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,h,g){function l(a,b){var c=h.data("$classCounts")||fa(), -d=[];m(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});h.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||f.$index%2===a){var k=e(b||[]);if(!n){var m=l(k,1);g.$addClass(m)}else if(!ka(b,n)){var s=e(n),m=d(k,s),k=d(s,k),m=l(m,1),k=l(k,-1);m&&m.length&&c.addClass(h,m);k&&k.length&&c.removeClass(h,k)}}n=ja(b)}var n;f.$watch(g[b],k,!0);g.$observe("class",function(a){k(f.$eval(g[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var h=c&1;if(h!==(d&1)){var k= -e(f.$eval(g[b]));h===a?(h=l(k,1),g.$addClass(h)):(h=l(k,-1),g.$removeClass(h))}})}}}]}function Hd(b){function a(a,b){b&&!f[a]?(l.addClass(e,a),f[a]=!0):!b&&f[a]&&(l.removeClass(e,a),f[a]=!1)}function c(b,c){b=b?"-"+Ac(b,"-"):"";a(mb+b,!0===c);a(Kd+b,!1===c)}var d=b.ctrl,e=b.$element,f={},h=b.set,g=b.unset,l=b.$animate;f[Kd]=!(f[mb]=e.hasClass(mb));d.$setValidity=function(b,e,f){v(e)?(d.$pending||(d.$pending={}),h(d.$pending,b,f)):(d.$pending&&g(d.$pending,b,f),Ld(d.$pending)&&(d.$pending=w));bb(e)? -e?(g(d.$error,b,f),h(d.$$success,b,f)):(h(d.$error,b,f),g(d.$$success,b,f)):(g(d.$error,b,f),g(d.$$success,b,f));d.$pending?(a(Md,!0),d.$valid=d.$invalid=w,c("",null)):(a(Md,!1),d.$valid=Ld(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?w:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);d.$$parentForm.$setValidity(b,e,d)}}function Ld(b){if(b)for(var a in b)if(b.hasOwnProperty(a))return!1;return!0}var jg=/^\/(.+)\/([a-z]*)$/,F=function(b){return G(b)?b.toLowerCase():b},ta=Object.prototype.hasOwnProperty, -sb=function(b){return G(b)?b.toUpperCase():b},Wa,B,ra,ua=[].slice,Nf=[].splice,kg=[].push,va=Object.prototype.toString,rc=Object.getPrototypeOf,Ea=I("ng"),da=Q.angular||(Q.angular={}),Rb,nb=0;Wa=X.documentMode;y.$inject=[];$a.$inject=[];var J=Array.isArray,tc=/^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/,T=function(b){return G(b)?b.trim():b},vd=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Fa=function(){if(!A(Fa.rules)){var b= -X.querySelector("[ng-csp]")||X.querySelector("[data-ng-csp]");if(b){var a=b.getAttribute("ng-csp")||b.getAttribute("data-ng-csp");Fa.rules={noUnsafeEval:!a||-1!==a.indexOf("no-unsafe-eval"),noInlineStyle:!a||-1!==a.indexOf("no-inline-style")}}else{b=Fa;try{new Function(""),a=!1}catch(c){a=!0}b.rules={noUnsafeEval:a,noInlineStyle:!1}}}return Fa.rules},pb=function(){if(A(pb.name_))return pb.name_;var b,a,c=Qa.length,d,e;for(a=0;a<c;++a)if(d=Qa[a],b=X.querySelector("["+d.replace(":","\\:")+"jq]")){e= -b.getAttribute(d+"jq");break}return pb.name_=e},Qa=["ng-","data-ng-","ng:","x-ng-"],be=/[A-Z]/g,Bc=!1,Qb,pa=1,Pa=3,fe={full:"1.4.7",major:1,minor:4,dot:7,codeName:"dark-luminescence"};R.expando="ng339";var hb=R.cache={},Ff=1;R._data=function(b){return this.cache[b[this.expando]]||{}};var Af=/([\:\-\_]+(.))/g,Bf=/^moz([A-Z])/,lg={mouseleave:"mouseout",mouseenter:"mouseover"},Tb=I("jqLite"),Ef=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Sb=/<|&#?\w+;/,Cf=/<([\w:-]+)/,Df=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, -ma={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option;ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead;ma.th=ma.td;var Ra=R.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===X.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),R(Q).on("load",a))}, -toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?B(this[b]):B(this[this.length+b])},length:0,push:kg,sort:[].sort,splice:[].splice},Bb={};m("multiple selected checked disabled readOnly required open".split(" "),function(b){Bb[F(b)]=b});var Sc={};m("input select option textarea button form details".split(" "),function(b){Sc[b]=!0});var $c={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"}; -m({data:Vb,removeData:vb,hasData:function(b){for(var a in hb[b.ng339])return!0;return!1}},function(b,a){R[a]=b});m({data:Vb,inheritedData:Ab,scope:function(b){return B.data(b,"$scope")||Ab(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return B.data(b,"$isolateScope")||B.data(b,"$isolateScopeNoTemplate")},controller:Pc,injector:function(b){return Ab(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:xb,css:function(b,a,c){a=gb(a);if(A(c))b.style[a]=c;else return b.style[a]}, -attr:function(b,a,c){var d=b.nodeType;if(d!==Pa&&2!==d&&8!==d)if(d=F(a),Bb[d])if(A(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||y).specified?d:w;else if(A(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?w:b},prop:function(b,a,c){if(A(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(v(b)){var d=a.nodeType;return d===pa||d===Pa?a.textContent:""}a.textContent=b}b.$dv="";return b}(), -val:function(b,a){if(v(a)){if(b.multiple&&"select"===wa(b)){var c=[];m(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(v(a))return b.innerHTML;ub(b,!0);b.innerHTML=a},empty:Qc},function(b,a){R.prototype[a]=function(a,d){var e,f,h=this.length;if(b!==Qc&&v(2==b.length&&b!==xb&&b!==Pc?a:d)){if(C(a)){for(e=0;e<h;e++)if(b===Vb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;h=v(e)?Math.min(h,1):h; -for(f=0;f<h;f++){var g=b(this[f],a,d);e=e?e+g:g}return e}for(e=0;e<h;e++)b(this[e],a,d);return this}});m({removeData:vb,on:function a(c,d,e,f){if(A(f))throw Tb("onargs");if(Lc(c)){var h=wb(c,!0);f=h.events;var g=h.handle;g||(g=h.handle=Hf(c,f));for(var h=0<=d.indexOf(" ")?d.split(" "):[d],l=h.length;l--;){d=h[l];var k=f[d];k||(f[d]=[],"mouseenter"===d||"mouseleave"===d?a(c,lg[d],function(a){var c=a.relatedTarget;c&&(c===this||this.contains(c))||g(a,d)}):"$destroy"!==d&&c.addEventListener(d,g,!1), -k=f[d]);k.push(e)}}},off:Oc,one:function(a,c,d){a=B(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;ub(a);m(new R(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];m(a.childNodes,function(a){a.nodeType===pa&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(d===pa||11===d){c=new R(c);for(var d=0,e=c.length;d< -e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===pa){var d=a.firstChild;m(new R(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=B(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:Wb,detach:function(a){Wb(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new R(c);for(var f=0,h=c.length;f<h;f++){var g=c[f];e.insertBefore(g,d.nextSibling);d=g}},addClass:zb,removeClass:yb,toggleClass:function(a,c,d){c&&m(c.split(" "),function(c){var f= -d;v(f)&&(f=!xb(a,c));(f?zb:yb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Ub,triggerHandler:function(a,c,d){var e,f,h=c.type||c,g=wb(a);if(g=(g=g&&g.events)&&g[h])e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped= -!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:y,type:h,target:a},c.type&&(e=P(e,c)),c=ja(g),f=d?[e].concat(d):[e],m(c,function(c){e.isImmediatePropagationStopped()||c.apply(a,f)})}},function(a,c){R.prototype[c]=function(c,e,f){for(var h,g=0,l=this.length;g<l;g++)v(h)?(h=a(this[g],c,e,f),A(h)&&(h=B(h))):Nc(h,a(this[g],c,e,f));return A(h)?h:this};R.prototype.bind=R.prototype.on;R.prototype.unbind=R.prototype.off});Ua.prototype={put:function(a, -c){this[Ga(a,this.nextUid)]=c},get:function(a){return this[Ga(a,this.nextUid)]},remove:function(a){var c=this[a=Ga(a,this.nextUid)];delete this[a];return c}};var yf=[function(){this.$get=[function(){return Ua}]}],Uc=/^[^\(]*\(\s*([^\)]*)\)/m,mg=/,/,ng=/^\s*(_?)(\S+?)\1\s*$/,Tc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ha=I("$injector");fb.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=[];if(a.length){if(c)throw G(d)&&d||(d=a.name||If(a)),Ha("strictdi",d);c=a.toString().replace(Tc, -"");c=c.match(Uc);m(c[1].split(mg),function(a){a.replace(ng,function(a,c,d){e.push(d)})})}a.$inject=e}}else J(a)?(c=a.length-1,Sa(a[c],"fn"),e=a.slice(0,c)):Sa(a,"fn",!0);return e};var Nd=I("$animate"),Ue=function(){this.$get=["$q","$$rAF",function(a,c){function d(){}d.all=y;d.chain=y;d.prototype={end:y,cancel:y,resume:y,pause:y,complete:y,then:function(d,f){return a(function(a){c(function(){a()})}).then(d,f)}};return d}]},Te=function(){var a=new Ua,c=[];this.$get=["$$AnimateRunner","$rootScope", -function(d,e){function f(a,c,d){var e=!1;c&&(c=G(c)?c.split(" "):J(c)?c:[],m(c,function(c){c&&(e=!0,a[c]=d)}));return e}function h(){m(c,function(c){var d=a.get(c);if(d){var e=Jf(c.attr("class")),f="",h="";m(d,function(a,c){a!==!!e[c]&&(a?f+=(f.length?" ":"")+c:h+=(h.length?" ":"")+c)});m(c,function(a){f&&zb(a,f);h&&yb(a,h)});a.remove(c)}});c.length=0}return{enabled:y,on:y,off:y,pin:y,push:function(g,l,k,n){n&&n();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(l=k.addClass, -n=k.removeClass,k=a.get(g)||{},l=f(k,l,!0),n=f(k,n,!1),l||n)a.put(g,k),c.push(g),1===c.length&&e.$$postDigest(h);return new d}}}]},Re=["$provide",function(a){var c=this;this.$$registeredAnimations=Object.create(null);this.register=function(d,e){if(d&&"."!==d.charAt(0))throw Nd("notcsel",d);var f=d+"-animation";c.$$registeredAnimations[d.substr(1)]=f;a.factory(f,e)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Nd("nongcls", -"ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function c(a,d,e){if(e){var l;a:{for(l=0;l<e.length;l++){var k=e[l];if(1===k.nodeType){l=k;break a}}l=void 0}!l||l.parentNode||l.previousElementSibling||(e=null)}e?e.after(a):d.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(f,h,g,l){h=h&&B(h);g=g&&B(g);h=h||g.parent();c(f,h,g);return a.push(f,"enter",Ia(l))},move:function(f,h,g,l){h=h&&B(h);g=g&&B(g); -h=h||g.parent();c(f,h,g);return a.push(f,"move",Ia(l))},leave:function(c,e){return a.push(c,"leave",Ia(e),function(){c.remove()})},addClass:function(c,e,g){g=Ia(g);g.addClass=ib(g.addclass,e);return a.push(c,"addClass",g)},removeClass:function(c,e,g){g=Ia(g);g.removeClass=ib(g.removeClass,e);return a.push(c,"removeClass",g)},setClass:function(c,e,g,l){l=Ia(l);l.addClass=ib(l.addClass,e);l.removeClass=ib(l.removeClass,g);return a.push(c,"setClass",l)},animate:function(c,e,g,l,k){k=Ia(k);k.from=k.from? -P(k.from,e):e;k.to=k.to?P(k.to,g):g;k.tempClasses=ib(k.tempClasses,l||"ng-inline-animate");return a.push(c,"animate",k)}}}]}],Se=function(){this.$get=["$$rAF","$q",function(a,c){var d=function(){};d.prototype={done:function(a){this.defer&&this.defer[!0===a?"reject":"resolve"]()},end:function(){this.done()},cancel:function(){this.done(!0)},getPromise:function(){this.defer||(this.defer=c.defer());return this.defer.promise},then:function(a,c){return this.getPromise().then(a,c)},"catch":function(a){return this.getPromise()["catch"](a)}, -"finally":function(a){return this.getPromise()["finally"](a)}};return function(c,f){function h(){a(function(){f.addClass&&(c.addClass(f.addClass),f.addClass=null);f.removeClass&&(c.removeClass(f.removeClass),f.removeClass=null);f.to&&(c.css(f.to),f.to=null);g||l.done();g=!0});return l}f.cleanupStyles&&(f.from=f.to=null);f.from&&(c.css(f.from),f.from=null);var g,l=new d;return{start:h,end:h}}}]},ga=I("$compile");Dc.$inject=["$provide","$$sanitizeUriProvider"];var Wc=/^((?:x|data)[\:\-_])/i,Of=I("$controller"), -Vc=/^(\S+)(\s+as\s+(\w+))?$/,$e=function(){this.$get=["$document",function(a){return function(c){c?!c.nodeType&&c instanceof B&&(c=c[0]):c=a[0].body;return c.offsetWidth+1}}]},ad="application/json",$b={"Content-Type":ad+";charset=utf-8"},Qf=/^\[|^\{(?!\{)/,Rf={"[":/]$/,"{":/}$/},Pf=/^\)\]\}',?\n/,og=I("$http"),ed=function(a){return function(){throw og("legacy",a);}},La=da.$interpolateMinErr=I("$interpolate");La.throwNoconcat=function(a){throw La("noconcat",a);};La.interr=function(a,c){return La("interr", -a,c.toString())};var pg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Tf={http:80,https:443,ftp:21},Db=I("$location"),qg={$$html5:!1,$$replace:!1,absUrl:Eb("$$absUrl"),url:function(a){if(v(a))return this.$$url;var c=pg.exec(a);(c[1]||""===a)&&this.path(decodeURIComponent(c[1]));(c[2]||c[1]||""===a)&&this.search(c[3]||"");this.hash(c[5]||"");return this},protocol:Eb("$$protocol"),host:Eb("$$host"),port:Eb("$$port"),path:jd("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a, -c){switch(arguments.length){case 0:return this.$$search;case 1:if(G(a)||V(a))a=a.toString(),this.$$search=yc(a);else if(C(a))a=ha(a,{}),m(a,function(c,e){null==c&&delete a[e]}),this.$$search=a;else throw Db("isrcharg");break;default:v(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:jd("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};m([id,cc,bc],function(a){a.prototype=Object.create(qg);a.prototype.state= -function(c){if(!arguments.length)return this.$$state;if(a!==bc||!this.$$html5)throw Db("nostate");this.$$state=v(c)?null:c;return this}});var Z=I("$parse"),Uf=Function.prototype.call,Vf=Function.prototype.apply,Wf=Function.prototype.bind,Lb=fa();m("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var rg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},ec=function(a){this.options=a};ec.prototype={constructor:ec,lex:function(a){this.text=a;this.index=0;for(this.tokens= -[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;else{var c=a+this.peek(),d=c+this.peek(2),e=Lb[c],f=Lb[d];Lb[a]||e||f?(a=f?d:e?c:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+= -a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,c){return-1!==c.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"=== -a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=A(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw Z("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=F(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)|| -e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:c,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(!this.isIdent(c)&&!this.isNumber(c))break;this.index++}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var h= -this.text.charAt(this.index),e=e+h;if(f)"u"===h?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d+=rg[h]||h,f=!1;else if("\\"===h)f=!0;else{if(h===a){this.index++;this.tokens.push({index:c,text:e,constant:!0,value:d});return}d+=h}this.index++}this.throwError("Unterminated quote",c)}};var s=function(a,c){this.lexer=a;this.options=c};s.Program="Program";s.ExpressionStatement= -"ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal="Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.NGValueParameter="NGValueParameter"; -s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a= -this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),c,d;return this.expect("?")&&(c=this.expression(),this.consume(":"))?(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:c,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression, -operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:c.text, -left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()}, -primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=ha(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var c;c=this.expect("(","[",".");)"("===c.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()}, -this.consume(")")):"["===c.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===c.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var c={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return c},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression()); -while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],c;if("}"!==this.peekToken().text){do{if(this.peek("}"))break; -c={type:s.Property,kind:"init"};this.peek().constant?c.key=this.constant():this.peek().identifier?c.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");c.value=this.expression();a.push(c)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,c){throw Z("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},consume:function(a){if(0===this.tokens.length)throw Z("ueoe",this.text);var c=this.expect(a); -c||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return c},peekToken:function(){if(0===this.tokens.length)throw Z("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){return this.peekAhead(0,a,c,d,e)},peekAhead:function(a,c,d,e,f){if(this.tokens.length>a){a=this.tokens[a];var h=a.text;if(h===c||h===d||h===e||h===f||!(c||d||e||f))return a}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},constants:{"true":{type:s.Literal,value:!0}, -"false":{type:s.Literal,value:!1},"null":{type:s.Literal,value:null},undefined:{type:s.Literal,value:w},"this":{type:s.ThisExpression}}};sd.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:c,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};U(e,d.$filter);var f="",h;this.stage="assign";if(h=qd(e))this.state.computing="assign",f=this.nextId(),this.recurse(h,f),this.return_(f),f="fn.assign="+this.generateFunction("assign", -"s,v,l");h=od(e.body);d.stage="inputs";m(h,function(a,c){var e="fn"+c;d.state[e]={vars:[],body:[],own:{}};d.state.computing=e;var f=d.nextId();d.recurse(a,f);d.return_(f);d.state.inputs.push(e);a.watchId=c});this.state.computing="fn";this.stage="main";this.recurse(e);f='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+f+this.watchFns()+"return fn;";f=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue", -"ensureSafeAssignContext","ifDefined","plus","text",f))(this.$filter,Xa,Ba,ld,kd,md,Xf,nd,a);this.state=this.stage=w;f.literal=rd(e);f.constant=e.constant;return f},USE:"use",STRICT:"strict",watchFns:function(){var a=[],c=this.state.inputs,d=this;m(c,function(c){a.push("var "+c+"="+d.generateFunction(c,"s"))});c.length&&a.push("fn.inputs=["+c.join(",")+"];");return a.join("")},generateFunction:function(a,c){return"function("+c+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a= -[],c=this;m(this.state.filters,function(d,e){a.push(d+"=$filter("+c.escape(e)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,c,d,e,f,h){var g,l,k=this,n,p;e=e||y;if(!h&&A(a.watchId))c=c||this.nextId(),this.if_("i",this.lazyAssign(c,this.computedMember("i",a.watchId)),this.lazyRecurse(a,c,d,e,f,!0));else switch(a.type){case s.Program:m(a.body, -function(c,d){k.recurse(c.expression,w,w,function(a){l=a});d!==a.body.length-1?k.current().body.push(l,";"):k.return_(l)});break;case s.Literal:p=this.escape(a.value);this.assign(c,p);e(p);break;case s.UnaryExpression:this.recurse(a.argument,w,w,function(a){l=a});p=a.operator+"("+this.ifDefined(l,0)+")";this.assign(c,p);e(p);break;case s.BinaryExpression:this.recurse(a.left,w,w,function(a){g=a});this.recurse(a.right,w,w,function(a){l=a});p="+"===a.operator?this.plus(g,l):"-"===a.operator?this.ifDefined(g, -0)+a.operator+this.ifDefined(l,0):"("+g+")"+a.operator+"("+l+")";this.assign(c,p);e(p);break;case s.LogicalExpression:c=c||this.nextId();k.recurse(a.left,c);k.if_("&&"===a.operator?c:k.not(c),k.lazyRecurse(a.right,c));e(c);break;case s.ConditionalExpression:c=c||this.nextId();k.recurse(a.test,c);k.if_(c,k.lazyRecurse(a.alternate,c),k.lazyRecurse(a.consequent,c));e(c);break;case s.Identifier:c=c||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l", -a.name)+"?l:s"),d.computed=!1,d.name=a.name);Xa(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){f&&1!==f&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(c,k.nonComputedMember("s",a.name))})},c&&k.lazyAssign(c,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(c);e(c);break;case s.MemberExpression:g=d&&(d.context=this.nextId())|| -this.nextId();c=c||this.nextId();k.recurse(a.object,g,w,function(){k.if_(k.notNull(g),function(){if(a.computed)l=k.nextId(),k.recurse(a.property,l),k.getStringValue(l),k.addEnsureSafeMemberName(l),f&&1!==f&&k.if_(k.not(k.computedMember(g,l)),k.lazyAssign(k.computedMember(g,l),"{}")),p=k.ensureSafeObject(k.computedMember(g,l)),k.assign(c,p),d&&(d.computed=!0,d.name=l);else{Xa(a.property.name);f&&1!==f&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name), -"{}"));p=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))p=k.ensureSafeObject(p);k.assign(c,p);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(c,"undefined")});e(c)},!!f);break;case s.CallExpression:c=c||this.nextId();a.filter?(l=k.filter(a.callee.name),n=[],m(a.arguments,function(a){var c=k.nextId();k.recurse(a,c);n.push(c)}),p=l+"("+n.join(",")+")",k.assign(c,p),e(c)):(l=k.nextId(),g={},n=[],k.recurse(a.callee,l,g,function(){k.if_(k.notNull(l), -function(){k.addEnsureSafeFunction(l);m(a.arguments,function(a){k.recurse(a,k.nextId(),w,function(a){n.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),p=k.member(g.context,g.name,g.computed)+"("+n.join(",")+")"):p=l+"("+n.join(",")+")";p=k.ensureSafeObject(p);k.assign(c,p)},function(){k.assign(c,"undefined")});e(c)}));break;case s.AssignmentExpression:l=this.nextId();g={};if(!pd(a.left))throw Z("lval");this.recurse(a.left,w,g,function(){k.if_(k.notNull(g.context), -function(){k.recurse(a.right,l);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);p=k.member(g.context,g.name,g.computed)+a.operator+l;k.assign(c,p);e(c||p)})},1);break;case s.ArrayExpression:n=[];m(a.elements,function(a){k.recurse(a,k.nextId(),w,function(a){n.push(a)})});p="["+n.join(",")+"]";this.assign(c,p);e(p);break;case s.ObjectExpression:n=[];m(a.properties,function(a){k.recurse(a.value,k.nextId(),w,function(c){n.push(k.escape(a.key.type=== -s.Identifier?a.key.name:""+a.key.value)+":"+c)})});p="{"+n.join(",")+"}";this.assign(c,p);e(p);break;case s.ThisExpression:this.assign(c,"s");e("s");break;case s.NGValueParameter:this.assign(c,"v"),e("v")}},getHasOwnProperty:function(a,c){var d=a+"."+c,e=this.current().own;e.hasOwnProperty(d)||(e[d]=this.nextId(!1,a+"&&("+this.escape(c)+" in "+a+")"));return e[d]},assign:function(a,c){if(a)return this.current().body.push(a,"=",c,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]= -this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,c){return"ifDefined("+a+","+this.escape(c)+")"},plus:function(a,c){return"plus("+a+","+c+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,c,d){if(!0===a)c();else{var e=this.current().body;e.push("if(",a,"){");c();e.push("}");d&&(e.push("else{"),d(),e.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,c){return a+"."+c},computedMember:function(a, -c){return a+"["+c+"]"},member:function(a,c,d){return d?this.computedMember(a,c):this.nonComputedMember(a,c)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+ -a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+",text)")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+a+",text)"},lazyRecurse:function(a,c,d,e,f,h){var g=this;return function(){g.recurse(a,c,d,e,f,h)}},lazyAssign:function(a,c){var d=this;return function(){d.assign(a,c)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g, -stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(G(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(V(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw Z("esc");},nextId:function(a,c){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(c?"="+c:""));return d},current:function(){return this.state[this.state.computing]}}; -td.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=c;U(e,d.$filter);var f,h;if(f=qd(e))h=this.recurse(f);f=od(e.body);var g;f&&(g=[],m(f,function(a,c){var e=d.recurse(a);a.input=e;g.push(e);a.watchId=c}));var l=[];m(e.body,function(a){l.push(d.recurse(a.expression))});f=0===e.body.length?function(){}:1===e.body.length?l[0]:function(a,c){var d;m(l,function(e){d=e(a,c)});return d};h&&(f.assign=function(a,c,d){return h(a,d,c)});g&&(f.inputs= -g);f.literal=rd(e);f.constant=e.constant;return f},recurse:function(a,c,d){var e,f,h=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,c);case s.UnaryExpression:return f=this.recurse(a.argument),this["unary"+a.operator](f,c);case s.BinaryExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case s.LogicalExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e, -f,c);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),c);case s.Identifier:return Xa(a.name,h.expression),h.identifier(a.name,h.expensiveChecks||Fb(a.name),c,d,h.expression);case s.MemberExpression:return e=this.recurse(a.object,!1,!!d),a.computed||(Xa(a.property.name,h.expression),f=a.property.name),a.computed&&(f=this.recurse(a.property)),a.computed?this.computedMember(e,f,c,d,h.expression):this.nonComputedMember(e,f, -h.expensiveChecks,c,d,h.expression);case s.CallExpression:return g=[],m(a.arguments,function(a){g.push(h.recurse(a))}),a.filter&&(f=this.$filter(a.callee.name)),a.filter||(f=this.recurse(a.callee,!0)),a.filter?function(a,d,e,h){for(var r=[],m=0;m<g.length;++m)r.push(g[m](a,d,e,h));a=f.apply(w,r,h);return c?{context:w,name:w,value:a}:a}:function(a,d,e,p){var r=f(a,d,e,p),m;if(null!=r.value){Ba(r.context,h.expression);ld(r.value,h.expression);m=[];for(var s=0;s<g.length;++s)m.push(Ba(g[s](a,d,e,p), -h.expression));m=Ba(r.value.apply(r.context,m),h.expression)}return c?{value:m}:m};case s.AssignmentExpression:return e=this.recurse(a.left,!0,1),f=this.recurse(a.right),function(a,d,g,p){var r=e(a,d,g,p);a=f(a,d,g,p);Ba(r.value,h.expression);md(r.context);r.context[r.name]=a;return c?{value:a}:a};case s.ArrayExpression:return g=[],m(a.elements,function(a){g.push(h.recurse(a))}),function(a,d,e,f){for(var h=[],m=0;m<g.length;++m)h.push(g[m](a,d,e,f));return c?{value:h}:h};case s.ObjectExpression:return g= -[],m(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:h.recurse(a.value)})}),function(a,d,e,f){for(var h={},m=0;m<g.length;++m)h[g[m].key]=g[m].value(a,d,e,f);return c?{value:h}:h};case s.ThisExpression:return function(a){return c?{value:a}:a};case s.NGValueParameter:return function(a,d,e,f){return c?{value:e}:e}}},"unary+":function(a,c){return function(d,e,f,h){d=a(d,e,f,h);d=A(d)?+d:0;return c?{value:d}:d}},"unary-":function(a,c){return function(d,e, -f,h){d=a(d,e,f,h);d=A(d)?-d:0;return c?{value:d}:d}},"unary!":function(a,c){return function(d,e,f,h){d=!a(d,e,f,h);return c?{value:d}:d}},"binary+":function(a,c,d){return function(e,f,h,g){var l=a(e,f,h,g);e=c(e,f,h,g);l=nd(l,e);return d?{value:l}:l}},"binary-":function(a,c,d){return function(e,f,h,g){var l=a(e,f,h,g);e=c(e,f,h,g);l=(A(l)?l:0)-(A(e)?e:0);return d?{value:l}:l}},"binary*":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)*c(e,f,h,g);return d?{value:e}:e}},"binary/":function(a,c, -d){return function(e,f,h,g){e=a(e,f,h,g)/c(e,f,h,g);return d?{value:e}:e}},"binary%":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)%c(e,f,h,g);return d?{value:e}:e}},"binary===":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)===c(e,f,h,g);return d?{value:e}:e}},"binary!==":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)!==c(e,f,h,g);return d?{value:e}:e}},"binary==":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)==c(e,f,h,g);return d?{value:e}:e}},"binary!=":function(a,c, -d){return function(e,f,h,g){e=a(e,f,h,g)!=c(e,f,h,g);return d?{value:e}:e}},"binary<":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)<c(e,f,h,g);return d?{value:e}:e}},"binary>":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)>c(e,f,h,g);return d?{value:e}:e}},"binary<=":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)<=c(e,f,h,g);return d?{value:e}:e}},"binary>=":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)>=c(e,f,h,g);return d?{value:e}:e}},"binary&&":function(a,c,d){return function(e, -f,h,g){e=a(e,f,h,g)&&c(e,f,h,g);return d?{value:e}:e}},"binary||":function(a,c,d){return function(e,f,h,g){e=a(e,f,h,g)||c(e,f,h,g);return d?{value:e}:e}},"ternary?:":function(a,c,d,e){return function(f,h,g,l){f=a(f,h,g,l)?c(f,h,g,l):d(f,h,g,l);return e?{value:f}:f}},value:function(a,c){return function(){return c?{context:w,name:w,value:a}:a}},identifier:function(a,c,d,e,f){return function(h,g,l,k){h=g&&a in g?g:h;e&&1!==e&&h&&!h[a]&&(h[a]={});g=h?h[a]:w;c&&Ba(g,f);return d?{context:h,name:a,value:g}: -g}},computedMember:function(a,c,d,e,f){return function(h,g,l,k){var n=a(h,g,l,k),p,m;null!=n&&(p=c(h,g,l,k),p=kd(p),Xa(p,f),e&&1!==e&&n&&!n[p]&&(n[p]={}),m=n[p],Ba(m,f));return d?{context:n,name:p,value:m}:m}},nonComputedMember:function(a,c,d,e,f,h){return function(g,l,k,n){g=a(g,l,k,n);f&&1!==f&&g&&!g[c]&&(g[c]={});l=null!=g?g[c]:w;(d||Fb(c))&&Ba(l,h);return e?{context:g,name:c,value:l}:l}},inputs:function(a,c){return function(d,e,f,h){return h?h[c]:a(d,e,f)}}};var fc=function(a,c,d){this.lexer= -a;this.$filter=c;this.options=d;this.ast=new s(this.lexer);this.astCompiler=d.csp?new td(this.ast,c):new sd(this.ast,c)};fc.prototype={constructor:fc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};fa();fa();var Yf=Object.prototype.valueOf,Ca=I("$sce"),oa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ga=I("$compile"),$=X.createElement("a"),xd=Aa(Q.location.href);yd.$inject=["$document"];Kc.$inject=["$provide"];zd.$inject=["$locale"];Bd.$inject= -["$locale"];var hc=".",hg={yyyy:aa("FullYear",4),yy:aa("FullYear",2,0,!0),y:aa("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:aa("Month",2,1),M:aa("Month",1,1),dd:aa("Date",2),d:aa("Date",1),HH:aa("Hours",2),H:aa("Hours",1),hh:aa("Hours",2,-12),h:aa("Hours",1,-12),mm:aa("Minutes",2),m:aa("Minutes",1),ss:aa("Seconds",2),s:aa("Seconds",1),sss:aa("Milliseconds",3),EEEE:Hb("Day"),EEE:Hb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a,c,d){a=-1*d;return a=(0<= -a?"+":"")+(Gb(Math[0<a?"floor":"ceil"](a/60),2)+Gb(Math.abs(a%60),2))},ww:Fd(2),w:Fd(1),G:ic,GG:ic,GGG:ic,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},gg=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,fg=/^\-?\d+$/;Ad.$inject=["$locale"];var cg=qa(F),dg=qa(sb);Cd.$inject=["$parse"];var he=qa({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"=== -va.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),tb={};m(Bb,function(a,c){function d(a,d,f){a.$watch(f[e],function(a){f.$set(c,!!a)})}if("multiple"!=a){var e=ya("ng-"+c),f=d;"checked"===a&&(f=function(a,c,f){f.ngModel!==f[e]&&d(a,c,f)});tb[e]=function(){return{restrict:"A",priority:100,link:f}}}});m($c,function(a,c){tb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(jg))){f.$set("ngPattern", -new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});m(["src","srcset","href"],function(a){var c=ya("ng-"+a);tb[c]=function(){return{priority:99,link:function(d,e,f){var h=a,g=a;"href"===a&&"[object SVGAnimatedString]"===va.call(e.prop("href"))&&(g="xlinkHref",f.$attr[g]="xlink:href",h=null);f.$observe(c,function(c){c?(f.$set(g,c),Wa&&h&&e.prop(h,f[g])):"href"===a&&f.$set(g,null)})}}}});var Ib={$addControl:y,$$renameControl:function(a,c){a.$name=c},$removeControl:y,$setValidity:y, -$setDirty:y,$setPristine:y,$setSubmitted:y};Gd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Od=function(a){return["$timeout","$parse",function(c,d){function e(a){return""===a?d('this[""]').assign:d(a).assign||y}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Gd,compile:function(d,h){d.addClass(Ya).addClass(mb);var g=h.name?"name":a&&h.ngForm?"ngForm":!1;return{pre:function(a,d,f,h){var m=h[0];if(!("action"in f)){var t=function(c){a.$apply(function(){m.$commitViewValue(); -m.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",t,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",t,!1)},0,!1)})}(h[1]||m.$$parentForm).$addControl(m);var s=g?e(m.$name):y;g&&(s(a,m),f.$observe(g,function(c){m.$name!==c&&(s(a,w),m.$$parentForm.$$renameControl(m,c),s=e(m.$name),s(a,m))}));d.on("$destroy",function(){m.$$parentForm.$removeControl(m);s(a,w);P(m,Ib)})}}}}}]},ie=Od(),ve=Od(!0),ig=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/, -sg=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,tg=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,ug=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Pd=/^(\d{4})-(\d{2})-(\d{2})$/,Qd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,lc=/^(\d{4})-W(\d\d)$/,Rd=/^(\d{4})-(\d\d)$/,Sd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Td={text:function(a,c,d,e,f,h){jb(a,c,d,e,f,h);jc(e)},date:kb("date", -Pd,Kb(Pd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Qd,Kb(Qd,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Sd,Kb(Sd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",lc,function(a,c){if(ea(a))return a;if(G(a)){lc.lastIndex=0;var d=lc.exec(a);if(d){var e=+d[1],f=+d[2],h=d=0,g=0,l=0,k=Ed(e),f=7*(f-1);c&&(d=c.getHours(),h=c.getMinutes(),g=c.getSeconds(),l=c.getMilliseconds());return new Date(e,0,k.getDate()+f,d,h,g,l)}}return NaN},"yyyy-Www"), -month:kb("month",Rd,Kb(Rd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,h){Id(a,c,d,e);jb(a,c,d,e,f,h);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:ug.test(a)?parseFloat(a):w});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!V(a))throw lb("numfmt",a);a=a.toString()}return a});if(A(d.min)||d.ngMin){var g;e.$validators.min=function(a){return e.$isEmpty(a)||v(g)||a>=g};d.$observe("min",function(a){A(a)&&!V(a)&&(a=parseFloat(a,10));g=V(a)&&!isNaN(a)?a:w;e.$validate()})}if(A(d.max)|| -d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||v(l)||a<=l};d.$observe("max",function(a){A(a)&&!V(a)&&(a=parseFloat(a,10));l=V(a)&&!isNaN(a)?a:w;e.$validate()})}},url:function(a,c,d,e,f,h){jb(a,c,d,e,f,h);jc(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||sg.test(d)}},email:function(a,c,d,e,f,h){jb(a,c,d,e,f,h);jc(e);e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||tg.test(d)}},radio:function(a,c, -d,e){v(d.name)&&c.attr("name",++nb);c.on("click",function(a){c[0].checked&&e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,h,g,l){var k=Jd(l,a,"ngTrueValue",d.ngTrueValue,!0),n=Jd(l,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&&a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return!1===a};e.$formatters.push(function(a){return ka(a, -k)});e.$parsers.push(function(a){return a?k:n})},hidden:y,button:y,submit:y,reset:y,file:y},Ec=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,h,g,l){l[0]&&(Td[F(g.type)]||Td.text)(f,h,g,l[0],c,a,d,e)}}}}],vg=/^(true|false|\d+)$/,Ne=function(){return{restrict:"A",priority:100,compile:function(a,c){return vg.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value", -a)})}}}},ne=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=v(a)?"":a})}}}}],pe=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,h){d=a(f.attr(h.$attr.ngBindTemplate));c.$$addBindingInfo(f,d.expressions);f=f[0];h.$observe("ngBindTemplate",function(a){f.textContent=v(a)?"":a})}}}}],oe=["$sce","$parse", -"$compile",function(a,c,d){return{restrict:"A",compile:function(e,f){var h=c(f.ngBindHtml),g=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(g,function(){e.html(a.getTrustedHtml(h(c))||"")})}}}}],Me=qa({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),qe=kc("",!0),se=kc("Odd",0),re=kc("Even",1),te=Na({compile:function(a,c){c.$set("ngCloak", -w);a.removeClass("ng-cloak")}}),ue=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Jc={},wg={blur:!0,focus:!0};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=ya("ng-"+a);Jc[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,h){var g=d(h[c],null,!0);return function(c,d){d.on(a,function(d){var f=function(){g(c,{$event:d})}; -wg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var xe=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,h){var g,l,k;c.$watch(e.ngIf,function(c){c?l||h(function(c,f){l=f;c[c.length++]=X.createComment(" end ngIf: "+e.ngIf+" ");g={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),g&&(k=rb(g.clone),a.leave(k).then(function(){k=null}),g=null))})}}}],ye=["$templateRequest","$anchorScroll", -"$animate",function(a,c,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:da.noop,compile:function(e,f){var h=f.ngInclude||f.src,g=f.onload||"",l=f.autoscroll;return function(e,f,m,r,t){var s=0,v,u,q,z=function(){u&&(u.remove(),u=null);v&&(v.$destroy(),v=null);q&&(d.leave(q).then(function(){u=null}),u=q,q=null)};e.$watch(h,function(h){var m=function(){!A(l)||l&&!e.$eval(l)||c()},p=++s;h?(a(h,!0).then(function(a){if(p===s){var c=e.$new();r.template=a;a=t(c,function(a){z(); -d.enter(a,null,f).then(m)});v=c;q=a;v.$emit("$includeContentLoaded",h);e.$eval(g)}},function(){p===s&&(z(),e.$emit("$includeContentError",h))}),e.$emit("$includeContentRequested",h)):(z(),r.template=null)})}}}}],Pe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Mc(f.template,X).childNodes)(c,function(a){d.append(a)},{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ze=Na({priority:450, -compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Le=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",h="false"!==d.ngTrim,g=h?T(f):f;e.$parsers.push(function(a){if(!v(a)){var c=[];a&&m(a.split(g),function(a){a&&c.push(h?T(a):a)});return c}});e.$formatters.push(function(a){return J(a)?a.join(f):w});e.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",Kd="ng-invalid",Ya="ng-pristine",Jb="ng-dirty",Md= -"ng-pending",lb=I("ngModel"),xg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,h,g,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=w;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending= -w;this.$name=n(d.name||"",!1)(a);this.$$parentForm=Ib;var p=f(d.ngModel),r=p.assign,t=p,s=r,K=null,u,q=this;this.$$setOptions=function(a){if((q.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");t=function(a){var d=p(a);x(d)&&(d=c(a));return d};s=function(a,c){x(p(a))?g(a,{$$$p:q.$modelValue}):r(a,q.$modelValue)}}else if(!p.assign)throw lb("nonassign",d.ngModel,xa(e));};this.$render=y;this.$isEmpty=function(a){return v(a)||""===a||null===a||a!==a};var z=0;Hd({ctrl:this,$element:e, -set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},$animate:h});this.$setPristine=function(){q.$dirty=!1;q.$pristine=!0;h.removeClass(e,Jb);h.addClass(e,Ya)};this.$setDirty=function(){q.$dirty=!0;q.$pristine=!1;h.removeClass(e,Ya);h.addClass(e,Jb);q.$$parentForm.$setDirty()};this.$setUntouched=function(){q.$touched=!1;q.$untouched=!0;h.setClass(e,"ng-untouched","ng-touched")};this.$setTouched=function(){q.$touched=!0;q.$untouched=!1;h.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue= -function(){g.cancel(K);q.$viewValue=q.$$lastCommittedViewValue;q.$render()};this.$validate=function(){if(!V(q.$modelValue)||!isNaN(q.$modelValue)){var a=q.$$rawModelValue,c=q.$valid,d=q.$modelValue,e=q.$options&&q.$options.allowInvalid;q.$$runValidators(a,q.$$lastCommittedViewValue,function(f){e||c===f||(q.$modelValue=f?a:w,q.$modelValue!==d&&q.$$writeModelToScope())})}};this.$$runValidators=function(a,c,d){function e(){var d=!0;m(q.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d? -!0:(m(q.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;m(q.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!x(k.then))throw lb("$asyncValidators",k);g(h,w);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},y):h(!0)}function g(a,c){l===z&&q.$setValidity(a,c)}function h(a){l===z&&d(a)}z++;var l=z;(function(){var a=q.$$parserName||"parse";if(v(u))g(a,null);else return u||(m(q.$validators,function(a,c){g(c,null)}),m(q.$asyncValidators, -function(a,c){g(c,null)})),g(a,u),u;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=q.$viewValue;g.cancel(K);if(q.$$lastCommittedViewValue!==a||""===a&&q.$$hasNativeValidators)q.$$lastCommittedViewValue=a,q.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=q.$$lastCommittedViewValue;if(u=v(c)?w:!0)for(var d=0;d<q.$parsers.length;d++)if(c=q.$parsers[d](c),v(c)){u=!1;break}V(q.$modelValue)&&isNaN(q.$modelValue)&&(q.$modelValue=t(a)); -var e=q.$modelValue,f=q.$options&&q.$options.allowInvalid;q.$$rawModelValue=c;f&&(q.$modelValue=c,q.$modelValue!==e&&q.$$writeModelToScope());q.$$runValidators(c,q.$$lastCommittedViewValue,function(a){f||(q.$modelValue=a?c:w,q.$modelValue!==e&&q.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,q.$modelValue);m(q.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};this.$setViewValue=function(a,c){q.$viewValue=a;q.$options&&!q.$options.updateOnDefault||q.$$debounceViewValueCommit(c)}; -this.$$debounceViewValueCommit=function(c){var d=0,e=q.$options;e&&A(e.debounce)&&(e=e.debounce,V(e)?d=e:V(e[c])?d=e[c]:V(e["default"])&&(d=e["default"]));g.cancel(K);d?K=g(function(){q.$commitViewValue()},d):l.$$phase?q.$commitViewValue():a.$apply(function(){q.$commitViewValue()})};a.$watch(function(){var c=t(a);if(c!==q.$modelValue&&(q.$modelValue===q.$modelValue||c===c)){q.$modelValue=q.$$rawModelValue=c;u=w;for(var d=q.$formatters,e=d.length,f=c;e--;)f=d[e](f);q.$viewValue!==f&&(q.$viewValue= -q.$$lastCommittedViewValue=f,q.$render(),q.$$runValidators(c,f,y))}return c})}],Ke=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:xg,priority:1,compile:function(c){c.addClass(Ya).addClass("ng-untouched").addClass(mb);return{pre:function(a,c,f,h){var g=h[0];c=h[1]||g.$$parentForm;g.$$setOptions(h[2]&&h[2].$options);c.$addControl(g);f.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})}, -post:function(c,e,f,h){var g=h[0];if(g.$options&&g.$options.updateOn)e.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});e.on("blur",function(e){g.$touched||(a.$$phase?c.$evalAsync(g.$setTouched):c.$apply(g.$setTouched))})}}}}}],yg=/(\s+|^)default(\s+|$)/,Oe=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=ha(a.$eval(c.ngModelOptions));A(this.$options.updateOn)?(this.$options.updateOnDefault=!1,this.$options.updateOn=T(this.$options.updateOn.replace(yg, -function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ae=Na({terminal:!0,priority:1E3}),zg=I("ngOptions"),Ag=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,Ie=["$compile","$parse",function(a,c){function d(a,d,e){function f(a,c,d,e,g){this.selectValue=a;this.viewValue=c;this.label= -d;this.group=e;this.disabled=g}function n(a){var c;if(!s&&Da(a))c=a;else{c=[];for(var d in a)a.hasOwnProperty(d)&&"$"!==d.charAt(0)&&c.push(d)}return c}var m=a.match(Ag);if(!m)throw zg("iexp",a,xa(d));var r=m[5]||m[7],s=m[6];a=/ as /.test(m[0])&&m[1];var v=m[9];d=c(m[2]?m[1]:r);var w=a&&c(a)||d,u=v&&c(v),q=v?function(a,c){return u(e,c)}:function(a){return Ga(a)},z=function(a,c){return q(a,x(a,c))},y=c(m[2]||m[1]),A=c(m[3]||""),O=c(m[4]||""),H=c(m[8]),B={},x=s?function(a,c){B[s]=c;B[r]=a;return B}: -function(a){B[r]=a;return B};return{trackBy:v,getTrackByValue:z,getWatchables:c(H,function(a){var c=[];a=a||[];for(var d=n(a),f=d.length,g=0;g<f;g++){var h=a===d?g:d[g],k=x(a[h],h),h=q(a[h],k);c.push(h);if(m[2]||m[1])h=y(e,k),c.push(h);m[4]&&(k=O(e,k),c.push(k))}return c}),getOptions:function(){for(var a=[],c={},d=H(e)||[],g=n(d),h=g.length,m=0;m<h;m++){var p=d===g?m:g[m],r=x(d[p],p),s=w(e,r),p=q(s,r),t=y(e,r),u=A(e,r),r=O(e,r),s=new f(p,s,t,u,r);a.push(s);c[p]=s}return{items:a,selectValueMap:c,getOptionFromViewValue:function(a){return c[z(a)]}, -getViewValueFromOption:function(a){return v?da.copy(a.viewValue):a.viewValue}}}}}var e=X.createElement("option"),f=X.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","?ngModel"],link:function(c,g,l,k){function n(a,c){a.element=c;c.disabled=a.disabled;a.label!==c.label&&(c.label=a.label,c.textContent=a.label);a.value!==c.value&&(c.value=a.selectValue)}function p(a,c,d,e){c&&F(c.nodeName)===d?d=c:(d=e.cloneNode(!1),c?a.insertBefore(d,c):a.appendChild(d));return d}function r(a){for(var c;a;)c= -a.nextSibling,Wb(a),a=c}function s(a){var c=q&&q[0],d=H&&H[0];if(c||d)for(;a&&(a===c||a===d||c&&8===c.nodeType);)a=a.nextSibling;return a}function v(){var a=x&&u.readValue();x=C.getOptions();var c={},d=g[0].firstChild;O&&g.prepend(q);d=s(d);x.items.forEach(function(a){var h,k;a.group?(h=c[a.group],h||(h=p(g[0],d,"optgroup",f),d=h.nextSibling,h.label=a.group,h=c[a.group]={groupElement:h,currentOptionElement:h.firstChild}),k=p(h.groupElement,h.currentOptionElement,"option",e),n(a,k),h.currentOptionElement= -k.nextSibling):(k=p(g[0],d,"option",e),n(a,k),d=k.nextSibling)});Object.keys(c).forEach(function(a){r(c[a].currentOptionElement)});r(d);w.$render();if(!w.$isEmpty(a)){var h=u.readValue();(C.trackBy?ka(a,h):a===h)||(w.$setViewValue(h),w.$render())}}var w=k[1];if(w){var u=k[0];k=l.multiple;for(var q,z=0,y=g.children(),A=y.length;z<A;z++)if(""===y[z].value){q=y.eq(z);break}var O=!!q,H=B(e.cloneNode(!1));H.val("?");var x,C=d(l.ngOptions,g,c);k?(w.$isEmpty=function(a){return!a||0===a.length},u.writeValue= -function(a){x.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){(a=x.getOptionFromViewValue(a))&&!a.disabled&&(a.element.selected=!0)})},u.readValue=function(){var a=g.val()||[],c=[];m(a,function(a){(a=x.selectValueMap[a])&&!a.disabled&&c.push(x.getViewValueFromOption(a))});return c},C.trackBy&&c.$watchCollection(function(){if(J(w.$viewValue))return w.$viewValue.map(function(a){return C.getTrackByValue(a)})},function(){w.$render()})):(u.writeValue=function(a){var c=x.getOptionFromViewValue(a); -c&&!c.disabled?g[0].value!==c.selectValue&&(H.remove(),O||q.remove(),g[0].value=c.selectValue,c.element.selected=!0,c.element.setAttribute("selected","selected")):null===a||O?(H.remove(),O||g.prepend(q),g.val(""),q.prop("selected",!0),q.attr("selected",!0)):(O||q.remove(),g.prepend(H),g.val("?"),H.prop("selected",!0),H.attr("selected",!0))},u.readValue=function(){var a=x.selectValueMap[g.val()];return a&&!a.disabled?(O||q.remove(),H.remove(),x.getViewValueFromOption(a)):null},C.trackBy&&c.$watch(function(){return C.getTrackByValue(w.$viewValue)}, -function(){w.$render()}));O?(q.remove(),a(q)(c),q.removeClass("ng-scope")):q=B(e.cloneNode(!1));v();c.$watchCollection(C.getWatchables,v)}}}}],Be=["$locale","$interpolate","$log",function(a,c,d){var e=/{}/g,f=/^when(Minus)?(.+)$/;return{link:function(h,g,l){function k(a){g.text(a||"")}var n=l.count,p=l.$attr.when&&g.attr(l.$attr.when),r=l.offset||0,s=h.$eval(p)||{},w={},A=c.startSymbol(),u=c.endSymbol(),q=A+n+"-"+r+u,z=da.noop,x;m(l,function(a,c){var d=f.exec(c);d&&(d=(d[1]?"-":"")+F(d[2]),s[d]=g.attr(l.$attr[c]))}); -m(s,function(a,d){w[d]=c(a.replace(e,q))});h.$watch(n,function(c){var e=parseFloat(c),f=isNaN(e);f||e in s||(e=a.pluralCat(e-r));e===x||f&&V(x)&&isNaN(x)||(z(),f=w[e],v(f)?(null!=c&&d.debug("ngPluralize: no rule defined for '"+e+"' in "+p),z=y,k()):z=h.$watch(f,k),x=e)})}}}],Ce=["$parse","$animate",function(a,c){var d=I("ngRepeat"),e=function(a,c,d,e,k,m,p){a[d]=e;k&&(a[k]=m);a.$index=c;a.$first=0===c;a.$last=c===p-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(c&1))};return{restrict:"A", -multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,h){var g=h.ngRepeat,l=X.createComment(" end ngRepeat: "+g+" "),k=g.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!k)throw d("iexp",g);var n=k[1],p=k[2],r=k[3],s=k[4],k=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",n);var v=k[3]||k[1],y=k[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw d("badident", -r);var u,q,z,A,x={$id:Ga};s?u=a(s):(z=function(a,c){return Ga(c)},A=function(a){return a});return function(a,f,h,k,n){u&&(q=function(c,d,e){y&&(x[y]=c);x[v]=d;x.$index=e;return u(a,x)});var s=fa();a.$watchCollection(p,function(h){var k,p,t=f[0],u,x=fa(),C,G,J,M,I,F,L;r&&(a[r]=h);if(Da(h))I=h,p=q||z;else for(L in p=q||A,I=[],h)ta.call(h,L)&&"$"!==L.charAt(0)&&I.push(L);C=I.length;L=Array(C);for(k=0;k<C;k++)if(G=h===I?k:I[k],J=h[G],M=p(G,J,k),s[M])F=s[M],delete s[M],x[M]=F,L[k]=F;else{if(x[M])throw m(L, -function(a){a&&a.scope&&(s[a.id]=a)}),d("dupes",g,M,J);L[k]={id:M,scope:w,clone:w};x[M]=!0}for(u in s){F=s[u];M=rb(F.clone);c.leave(M);if(M[0].parentNode)for(k=0,p=M.length;k<p;k++)M[k].$$NG_REMOVED=!0;F.scope.$destroy()}for(k=0;k<C;k++)if(G=h===I?k:I[k],J=h[G],F=L[k],F.scope){u=t;do u=u.nextSibling;while(u&&u.$$NG_REMOVED);F.clone[0]!=u&&c.move(rb(F.clone),null,B(t));t=F.clone[F.clone.length-1];e(F.scope,k,v,J,y,G,C)}else n(function(a,d){F.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a, -null,B(t));t=f;F.clone=a;x[F.id]=F;e(F.scope,k,v,J,y,G,C)});s=x})}}}}],De=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngShow,function(c){a[c?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],we=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngHide,function(c){a[c?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ee=Na(function(a,c,d){a.$watch(d.ngStyle, -function(a,d){d&&a!==d&&m(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Fe=["$animate",function(a){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var h=[],g=[],l=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=l.length;d<e;++d)a.cancel(l[d]);d=l.length=0;for(e=k.length;d<e;++d){var s=rb(g[d].clone);k[d].$destroy();(l[d]=a.leave(s)).then(n(l,d))}g.length=0;k.length=0;(h=f.cases["!"+ -c]||f.cases["?"])&&m(h,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=X.createComment(" end ngSwitchWhen: ");g.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Ge=Na({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),He=Na({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a, -c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),Je=Na({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw I("ngTransclude")("orphan",xa(c));f(function(a){c.empty();c.append(a)})}}),je=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],Bg={$setViewValue:y,$render:y},Cg=["$element","$scope","$attrs",function(a,c,d){var e=this,f=new Ua;e.ngModelCtrl=Bg;e.unknownOption=B(X.createElement("option")); -e.renderUnknownOption=function(c){c="? "+Ga(c)+" ?";e.unknownOption.val(c);a.prepend(e.unknownOption);a.val(c)};c.$on("$destroy",function(){e.renderUnknownOption=y});e.removeUnknownOption=function(){e.unknownOption.parent()&&e.unknownOption.remove()};e.readValue=function(){e.removeUnknownOption();return a.val()};e.writeValue=function(c){e.hasOption(c)?(e.removeUnknownOption(),a.val(c),""===c&&e.emptyOption.prop("selected",!0)):null==c&&e.emptyOption?(e.removeUnknownOption(),a.val("")):e.renderUnknownOption(c)}; -e.addOption=function(a,c){Ta(a,'"option value"');""===a&&(e.emptyOption=c);var d=f.get(a)||0;f.put(a,d+1)};e.removeOption=function(a){var c=f.get(a);c&&(1===c?(f.remove(a),""===a&&(e.emptyOption=w)):f.put(a,c-1))};e.hasOption=function(a){return!!f.get(a)}}],ke=function(){return{restrict:"E",require:["select","?ngModel"],controller:Cg,link:function(a,c,d,e){var f=e[1];if(f){var h=e[0];h.ngModelCtrl=f;f.$render=function(){h.writeValue(f.$viewValue)};c.on("change",function(){a.$apply(function(){f.$setViewValue(h.readValue())})}); -if(d.multiple){h.readValue=function(){var a=[];m(c.find("option"),function(c){c.selected&&a.push(c.value)});return a};h.writeValue=function(a){var d=new Ua(a);m(c.find("option"),function(a){a.selected=A(d.get(a.value))})};var g,l=NaN;a.$watch(function(){l!==f.$viewValue||ka(g,f.$viewValue)||(g=ja(f.$viewValue),f.$render());l=f.$viewValue});f.$isEmpty=function(a){return!a||0===a.length}}}}}},me=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(c,d){if(A(d.value))var e=a(d.value, -!0);else{var f=a(c.text(),!0);f||d.$set("value",c.text())}return function(a,c,d){function k(a){p.addOption(a,c);p.ngModelCtrl.$render();c[0].hasAttribute("selected")&&(c[0].selected=!0)}var m=c.parent(),p=m.data("$selectController")||m.parent().data("$selectController");if(p&&p.ngModelCtrl){if(e){var r;d.$observe("value",function(a){A(r)&&p.removeOption(r);r=a;k(a)})}else f?a.$watch(f,function(a,c){d.$set("value",a);c!==a&&p.removeOption(c);k(a)}):k(d.value);c.on("$destroy",function(){p.removeOption(d.value); -p.ngModelCtrl.$render()})}}}}}],le=qa({restrict:"E",terminal:!1}),Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){e&&(d.required=!0,e.$validators.required=function(a,c){return!d.required||!e.$isEmpty(c)},d.$observe("required",function(){e.$validate()}))}}},Fc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f,h=d.ngPattern||d.pattern;d.$observe("pattern",function(a){G(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw I("ngPattern")("noregexp", -h,a,xa(c));f=a||w;e.$validate()});e.$validators.pattern=function(a,c){return e.$isEmpty(c)||v(f)||f.test(c)}}}}},Ic=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=Y(a);f=isNaN(a)?-1:a;e.$validate()});e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=Y(a)||0;e.$validate()}); -e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};Q.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(ce(),ee(da),da.module("ngLocale",[],["$provide",function(a){function c(a){a+="";var c=a.indexOf(".");return-1==c?0:a.length-c-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "), -SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3, -maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",pluralCat:function(a,e){var f=a|0,h=e;w===h&&(h=Math.min(c(a),3));Math.pow(10,h);return 1==f&&0==h?"one":"other"}})}]),B(X).ready(function(){Zd(X,zc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>'); +(function(v){'use strict';function O(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.5/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function ya(a){if(null==a||Va(a))return!1;if(K(a)||F(a)||B&&a instanceof B)return!0; +var b="length"in Object(a)&&a.length;return Q(b)&&(0<=b&&(b-1 in a||a instanceof Array)||"function"==typeof a.item)}function q(a,b,d){var c,e;if(a)if(E(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(K(a)||ya(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==q)a.forEach(b,d,a);else if(oc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&& +b.call(d,a[c],c,a);else for(c in a)ua.call(a,c)&&b.call(d,a[c],c,a);return a}function pc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function qc(a){return function(b,d){a(d,b)}}function Xd(){return++nb}function Nb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(G(g)||E(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var n=h[k],m=g[n];d&&G(m)?fa(m)?a[n]=new Date(m.valueOf()):Wa(m)?a[n]=new RegExp(m):m.nodeName?a[n]=m.cloneNode(!0): +Ob(m)?a[n]=m.clone():(G(a[n])||(a[n]=K(m)?[]:{}),Nb(a[n],[m],!0)):a[n]=m}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function R(a){return Nb(a,za.call(arguments,1),!1)}function Yd(a){return Nb(a,za.call(arguments,1),!0)}function X(a){return parseInt(a,10)}function Pb(a,b){return R(Object.create(a),b)}function C(){}function Xa(a){return a}function da(a){return function(){return a}}function rc(a){return E(a.toString)&&a.toString!==ma}function y(a){return"undefined"===typeof a}function x(a){return"undefined"!== +typeof a}function G(a){return null!==a&&"object"===typeof a}function oc(a){return null!==a&&"object"===typeof a&&!sc(a)}function F(a){return"string"===typeof a}function Q(a){return"number"===typeof a}function fa(a){return"[object Date]"===ma.call(a)}function E(a){return"function"===typeof a}function Wa(a){return"[object RegExp]"===ma.call(a)}function Va(a){return a&&a.window===a}function Ya(a){return a&&a.$evalAsync&&a.$watch}function Da(a){return"boolean"===typeof a}function Zd(a){return a&&Q(a.length)&& +$d.test(ma.call(a))}function Ob(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function ae(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function va(a){return P(a.nodeName||a[0]&&a[0].nodeName)}function Za(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function qa(a,b){function d(a,b){var d=b.$$hashKey,e;if(K(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(oc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&& +(b[e]=c(a[e]));else for(e in a)ua.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!G(a))return a;var b=f.indexOf(a);if(-1!==b)return g[b];if(Va(a)||Ya(a))throw Aa("cpws");var b=!1,c=e(a);void 0===c&&(c=K(a)?[]:Object.create(sc(a)),b=!0);f.push(a);g.push(c);return b?d(a,c):c}function e(a){switch(ma.call(a)){case "[object Int8Array]":case "[object Int16Array]":case "[object Int32Array]":case "[object Float32Array]":case "[object Float64Array]":case "[object Uint8Array]":case "[object Uint8ClampedArray]":case "[object Uint16Array]":case "[object Uint32Array]":return new a.constructor(c(a.buffer)); +case "[object ArrayBuffer]":if(!a.slice){var b=new ArrayBuffer(a.byteLength);(new Uint8Array(b)).set(new Uint8Array(a));return b}return a.slice(0);case "[object Boolean]":case "[object Number]":case "[object String]":case "[object Date]":return new a.constructor(a.valueOf());case "[object RegExp]":return b=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),b.lastIndex=a.lastIndex,b;case "[object Blob]":return new a.constructor([a],{type:a.type})}if(E(a.cloneNode))return a.cloneNode(!0)}var f=[], +g=[];if(b){if(Zd(b)||"[object ArrayBuffer]"===ma.call(b))throw Aa("cpta");if(a===b)throw Aa("cpi");K(b)?b.length=0:q(b,function(a,d){"$$hashKey"!==d&&delete b[d]});f.push(a);g.push(b);return d(a,b)}return c(a)}function ha(a,b){if(K(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(G(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function pa(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&& +"object"==d)if(K(a)){if(!K(b))return!1;if((d=a.length)==b.length){for(c=0;c<d;c++)if(!pa(a[c],b[c]))return!1;return!0}}else{if(fa(a))return fa(b)?pa(a.getTime(),b.getTime()):!1;if(Wa(a))return Wa(b)?a.toString()==b.toString():!1;if(Ya(a)||Ya(b)||Va(a)||Va(b)||K(b)||fa(b)||Wa(b))return!1;d=T();for(c in a)if("$"!==c.charAt(0)&&!E(a[c])){if(!pa(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&x(b[c])&&!E(b[c]))return!1;return!0}return!1}function $a(a,b,d){return a.concat(za.call(b, +d))}function tc(a,b){var d=2<arguments.length?za.call(arguments,2):[];return!E(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,$a(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function be(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=void 0:Va(b)?d="$WINDOW":b&&v.document===b?d="$DOCUMENT":Ya(b)&&(d="$SCOPE");return d}function ab(a,b){if(!y(a))return Q(b)||(b=b?2:null),JSON.stringify(a,be, +b)}function uc(a){return F(a)?JSON.parse(a):a}function vc(a,b){a=a.replace(ce,"");var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Qb(a,b,d){d=d?-1:1;var c=a.getTimezoneOffset();b=vc(b,c);d*=b-c;a=new Date(a.getTime());a.setMinutes(a.getMinutes()+d);return a}function wa(a){a=B(a).clone();try{a.empty()}catch(b){}var d=B("<div>").append(a).html();try{return a[0].nodeType===Ma?P(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+P(b)})}catch(c){return P(d)}} +function wc(a){try{return decodeURIComponent(a)}catch(b){}}function xc(a){var b={};q((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),x(e)&&(f=x(f)?wc(f):!0,ua.call(b,e)?K(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Rb(a){var b=[];q(a,function(a,c){K(a)?q(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""} +function ob(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function de(a,b){var d,c,e=Na.length;for(c=0;c<e;++c)if(d=Na[c]+b,F(d=a.getAttribute(d)))return d;return null}function ee(a,b){var d,c,e={};q(Na,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))}); +q(Na,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":","\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==de(d,"strict-di"),b(d,c?[c]:[],e))}function yc(a,b,d){G(d)||(d={});d=R({strictDi:!1},d);var c=function(){a=B(a);if(a.injector()){var c=a[0]===v.document?"document":wa(a);throw Aa("btstrpd",c.replace(/</,"<").replace(/>/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]); +b.unshift("ng");c=bb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;v&&e.test(v.name)&&(d.debugInfoEnabled=!0,v.name=v.name.replace(e,""));if(v&&!f.test(v.name))return c();v.name=v.name.replace(f,"");ea.resumeBootstrap=function(a){q(a,function(a){b.push(a)});return c()};E(ea.resumeDeferredBootstrap)&&ea.resumeDeferredBootstrap()}function fe(){v.name= +"NG_ENABLE_DEBUG_INFO!"+v.name;v.location.reload()}function ge(a){a=ea.element(a).injector();if(!a)throw Aa("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(he,function(a,c){return(c?b:"")+a.toLowerCase()})}function ie(){var a;if(!Ac){var b=pb();(Z=y(b)?v.jQuery:b?v[b]:void 0)&&Z.fn.on?(B=Z,R(Z.fn,{scope:Oa.scope,isolateScope:Oa.isolateScope,controller:Oa.controller,injector:Oa.injector,inheritedData:Oa.inheritedData}),a=Z.cleanData,Z.cleanData=function(b){for(var c, +e=0,f;null!=(f=b[e]);e++)(c=Z._data(f,"events"))&&c.$destroy&&Z(f).triggerHandler("$destroy");a(b)}):B=U;ea.element=B;Ac=!0}}function qb(a,b,d){if(!a)throw Aa("areq",b||"?",d||"required");return a}function Pa(a,b,d){d&&K(a)&&(a=a[a.length-1]);qb(E(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Qa(a,b){if("hasOwnProperty"===a)throw Aa("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c= +b[g],a&&(a=(e=a)[c]);return!d&&E(a)?tc(e,a):a}function rb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=B(za.call(a,0,e))),c.push(b);return c||a}function T(){return Object.create(null)}function je(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=O("$injector"),c=O("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||O;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&& +(a[f]=null);return b(a,f,function(){function a(b,d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return M}}function b(a,d){return function(b,e){e&&E(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return M}}if(!g)throw d("nomod",f);var c=[],e=[],r=[],N=a("$injector","invoke","push",e),M={_invokeQueue:c,_configBlocks:e,_runBlocks:r,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide", +"constant","unshift"),decorator:b("$provide","decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),component:b("$compileProvider","component"),config:N,run:function(a){r.push(a);return this}};h&&N(h);return M})}})}function ke(a){R(a,{bootstrap:yc,copy:qa,extend:R,merge:Yd,equals:pa,element:B,forEach:q,injector:bb,noop:C,bind:tc,toJson:ab,fromJson:uc,identity:Xa,isUndefined:y, +isDefined:x,isString:F,isFunction:E,isObject:G,isNumber:Q,isElement:Ob,isArray:K,version:le,isDate:fa,lowercase:P,uppercase:sb,callbacks:{counter:0},getTestability:ge,$$minErr:O,$$csp:Ea,reloadWithDebugInfo:fe});Sb=je(v);Sb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:me});a.provider("$compile",Cc).directive({a:ne,input:Dc,textarea:Dc,form:oe,script:pe,select:qe,style:re,option:se,ngBind:te,ngBindHtml:ue,ngBindTemplate:ve,ngClass:we,ngClassEven:xe,ngClassOdd:ye,ngCloak:ze,ngController:Ae, +ngForm:Be,ngHide:Ce,ngIf:De,ngInclude:Ee,ngInit:Fe,ngNonBindable:Ge,ngPluralize:He,ngRepeat:Ie,ngShow:Je,ngStyle:Ke,ngSwitch:Le,ngSwitchWhen:Me,ngSwitchDefault:Ne,ngOptions:Oe,ngTransclude:Pe,ngModel:Qe,ngList:Re,ngChange:Se,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Te,ngModelOptions:Ue}).directive({ngInclude:Ve}).directive(tb).directive(Ic);a.provider({$anchorScroll:We,$animate:Xe,$animateCss:Ye,$$animateJs:Ze,$$animateQueue:$e, +$$AnimateRunner:af,$$animateAsyncRun:bf,$browser:cf,$cacheFactory:df,$controller:ef,$document:ff,$exceptionHandler:gf,$filter:Jc,$$forceReflow:hf,$interpolate:jf,$interval:kf,$http:lf,$httpParamSerializer:mf,$httpParamSerializerJQLike:nf,$httpBackend:of,$xhrFactory:pf,$location:qf,$log:rf,$parse:sf,$rootScope:tf,$q:uf,$$q:vf,$sce:wf,$sceDelegate:xf,$sniffer:yf,$templateCache:zf,$templateRequest:Af,$$testability:Bf,$timeout:Cf,$window:Df,$$rAF:Ef,$$jqLite:Ff,$$HashMap:Gf,$$cookieReader:Hf})}])}function cb(a){return a.replace(If, +function(a,d,c,e){return e?c.toUpperCase():c}).replace(Jf,"Moz$1")}function Kc(a){a=a.nodeType;return 1===a||!a||9===a}function Lc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Tb.test(a)){d=d||e.appendChild(b.createElement("div"));c=(Kf.exec(a)||["",""])[1].toLowerCase();c=ia[c]||ia._default;d.innerHTML=c[1]+a.replace(Lf,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=$a(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)}); +return e}function Mc(a,b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function U(a){if(a instanceof U)return a;var b;F(a)&&(a=V(a),b=!0);if(!(this instanceof U)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new U(a)}if(b){b=v.document;var d;a=(d=Mf.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Nc(this,a)}function Vb(a){return a.cloneNode(!0)}function ub(a,b){b||db(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)db(d[c])}function Oc(a, +b,d,c){if(x(c))throw Ub("offargs");var e=(c=vb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];x(d)&&Za(c||[],d);x(d)&&c&&0<c.length||(a.removeEventListener(b,f,!1),delete e[b])};q(b.split(" "),function(a){g(a);wb[a]&&g(wb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function db(a,b){var d=a.ng339,c=d&&eb[d];c&&(b?delete c.data[b]:(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Oc(a)),delete eb[d],a.ng339=void 0))}function vb(a,b){var d= +a.ng339,d=d&&eb[d];b&&!d&&(a.ng339=d=++Nf,d=eb[d]={events:{},data:{},handle:void 0});return d}function Wb(a,b,d){if(Kc(a)){var c=x(d),e=!c&&b&&!G(b),f=!b;a=(a=vb(a,!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];R(a,b)}}}function xb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function yb(a,b){b&&a.setAttribute&&q(b.split(" "),function(b){a.setAttribute("class",V((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g, +" ").replace(" "+V(b)+" "," ")))})}function zb(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(b.split(" "),function(a){a=V(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",V(d))}}function Nc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function Pc(a,b){return Ab(a,"$"+(b||"ngController")+"Controller")}function Ab(a, +b,d){9==a.nodeType&&(a=a.documentElement);for(b=K(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(x(d=B.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&a.host}}function Qc(a){for(ub(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Bb(a,b){b||ub(a);var d=a.parentNode;d&&d.removeChild(a)}function Of(a,b){b=b||v;if("complete"===b.document.readyState)b.setTimeout(a);else B(b).on("load",a)}function Rc(a,b){var d=Cb[b.toLowerCase()];return d&&Sc[va(a)]&&d}function Pf(a,b){var d=function(c, +d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(y(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||Qf;1<g&&(f=ha(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem= +a;return d}function Qf(a,b,d){d.call(a,b)}function Rf(a,b,d){var c=b.relatedTarget;c&&(c===a||Sf.call(a,c))||d.call(a,b)}function Ff(){this.$get=function(){return R(U,{hasClass:function(a,b){a.attr&&(a=a[0]);return xb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return yb(a,b)}})}}function Fa(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey= +d+":"+(b||Xd)():d+":"+a}function Ra(a,b){if(b){var d=0;this.nextUid=function(){return++d}}q(a,this.put,this)}function Tc(a){a=Function.prototype.toString.call(a).replace(Tf,"");return a.match(Uf)||a.match(Vf)}function Wf(a){return(a=Tc(a))?"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function bb(a,b){function d(a){return function(b,c){if(G(b))q(b,qc(a));else return a(b,c)}}function c(a,b){Qa(a,"service");if(E(b)||K(b))b=r.instantiate(b);if(!b.$get)throw Ga("pget",a);return m[a+"Provider"]= +b}function e(a,b){return function(){var c=w.invoke(b,this);if(y(c))throw Ga("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){qb(y(a)||K(a),"modulesToLoad","not an array");var b=[],c;q(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=r.get(e[0]);f[e[1]].apply(f,e[2])}}if(!n.get(a)){n.put(a,!0);try{F(a)?(c=Sb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):E(a)?b.push(r.invoke(a)):K(a)?b.push(r.invoke(a)): +Pa(a,"module")}catch(e){throw K(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ga("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===k)throw Ga("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,c,f){var g=[];a=bb.$$annotate(a,b,f);for(var h=0,k=a.length;h<k;h++){var l=a[h]; +if("string"!==typeof l)throw Ga("itkn",l);g.push(c&&c.hasOwnProperty(l)?c[l]:d(l,f))}return g}return{invoke:function(a,b,c,d){"string"===typeof c&&(d=c,c=null);c=e(a,c,d);K(a)&&(a=a[a.length-1]);d=11>=Ca?!1:"function"===typeof a&&/^(?:class\s|constructor\()/.test(Function.prototype.toString.call(a));return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=K(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d, +a))},get:d,annotate:bb.$$annotate,has:function(b){return m.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],n=new Ra([],!0),m={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,da(b),!1)}),constant:d(function(a,b){Qa(a,"constant");m[a]=b;N[a]=b}),decorator:function(a,b){var c=r.get(a+"Provider"),d=c.$get;c.$get=function(){var a=w.invoke(d,c);return w.invoke(b,null, +{$delegate:a})}}}},r=m.$injector=h(m,function(a,b){ea.isString(b)&&l.push(b);throw Ga("unpr",l.join(" <- "));}),N={},M=h(N,function(a,b){var c=r.get(a+"Provider",b);return w.invoke(c.$get,c,void 0,a)}),w=M;m.$injectorProvider={$get:da(M)};var p=g(a),w=M.get("$injector");w.strictDi=b;q(p,function(a){a&&w.invoke(a)});return w}function We(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a, +function(a){if("a"===va(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;E(c)?c=c():Ob(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Q(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=F(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a=== +b&&""===a||Of(function(){c.$evalAsync(g)})});return g}]}function fb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;K(a)&&(a=a.join(" "));K(b)&&(b=b.join(" "));return a+" "+b}function Xf(a){F(a)&&(a=a.split(" "));var b=T();q(a,function(a){a.length&&(b[a]=!0)});return b}function Ha(a){return G(a)?a:{}}function Yf(a,b,d,c){function e(a){try{a.apply(null,za.call(arguments,1))}finally{if(M--,0===M)for(;w.length;)try{w.pop()()}catch(b){d.error(b)}}}function f(){u=null;g();h()}function g(){p=I(); +p=y(p)?null:p;pa(p,L)&&(p=L);L=p}function h(){if(t!==k.url()||H!==p)t=k.url(),H=p,q(J,function(a){a(k.url(),p)})}var k=this,l=a.location,n=a.history,m=a.setTimeout,r=a.clearTimeout,N={};k.isMock=!1;var M=0,w=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){M++};k.notifyWhenNoOutstandingRequests=function(a){0===M?a():w.push(a)};var p,H,t=l.href,z=b.find("base"),u=null,I=c.history?function(){try{return n.state}catch(a){}}:C;g();H=p;k.url=function(b,d,e){y(e)&&(e=null);l!== +a.location&&(l=a.location);n!==a.history&&(n=a.history);if(b){var f=H===e;if(t===b&&(!c.history||f))return k;var h=t&&Ia(t)===Ia(b);t=b;H=e;if(!c.history||h&&f){if(!h||u)u=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(u=b)}else n[d?"replaceState":"pushState"](e,"",b),g(),H=p;return k}return u||l.href.replace(/%27/g,"'")};k.state=function(){return p};var J=[],D=!1,L=null;k.onUrlChange=function(b){if(!D){if(c.history)B(a).on("popstate",f);B(a).on("hashchange", +f);D=!0}J.push(b);return b};k.$$applicationDestroyed=function(){B(a).off("hashchange popstate",f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=z.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;M++;c=m(function(){delete N[c];e(a)},b||0);N[c]=!0;return c};k.defer.cancel=function(a){return N[a]?(delete N[a],r(a),e(C),!0):!1}}function cf(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Yf(a,c,b,d)}]}function df(){this.$get= +function(){function a(a,c){function e(a){a!=m&&(r?r==a&&(r=a.n):r=a,f(a.n,a.p),f(a,m),m=a,m.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw O("$cacheFactory")("iid",a);var g=0,h=R({},c,{id:a}),k=T(),l=c&&c.capacity||Number.MAX_VALUE,n=T(),m=null,r=null;return b[a]={put:function(a,b){if(!y(b)){if(l<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(r.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return k[a]}, +remove:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;b==m&&(m=b.p);b==r&&(r=b.n);f(b.n,b.p);delete n[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=T();g=0;n=T();m=r=null},destroy:function(){n=h=k=null;delete b[a]},info:function(){return R({},h,{size:g})}}}var b={};a.info=function(){var a={};q(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function zf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Cc(a,b){function d(a, +b,c){var d=/^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/,e=T();q(a,function(a,f){if(a in n)e[f]=n[a];else{var g=a.match(d);if(!g)throw ga("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f};g[4]&&(n[a]=e[f])}});return e}function c(a){var b=a.charAt(0);if(!b||b!==P(b))throw ga("baddir",a);if(a!==a.trim())throw ga("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/, +h=ae("ngSrc,ngSrcset,src,srcset"),k=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/,n=T();this.directive=function M(b,d){Qa(b,"directive");F(b)?(c(b),qb(d,"directiveFactory"),e.hasOwnProperty(b)||(e[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var d=[];q(e[b],function(e,f){try{var g=a.invoke(e);E(g)?g={compile:da(g)}:!g.compile&&g.link&&(g.compile=da(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||b;g.require=g.require||g.controller&&g.name;g.restrict= +g.restrict||"EA";g.$$moduleName=e.$$moduleName;d.push(g)}catch(h){c(h)}});return d}])),e[b].push(d)):q(b,qc(M));return this};this.component=function(a,b){function c(a){function e(b){return E(b)||K(b)?function(c,d){return a.invoke(b,this,{$element:c,$attrs:d})}:b}var f=b.template||b.templateUrl?b.template:"",g={controller:d,controllerAs:Uc(b.controller)||b.controllerAs||"$ctrl",template:e(f),templateUrl:e(b.templateUrl),transclude:b.transclude,scope:{},bindToController:b.bindings||{},restrict:"E", +require:b.require};q(b,function(a,b){"$"===b.charAt(0)&&(g[b]=a)});return g}var d=b.controller||function(){};q(b,function(a,b){"$"===b.charAt(0)&&(c[b]=a,E(d)&&(d[b]=a))});c.$inject=["$injector"];return this.directive(a,c)};this.aHrefSanitizationWhitelist=function(a){return x(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled= +function(a){return x(a)?(m=a,this):m};var r=10;this.onChangesTtl=function(a){return arguments.length?(r=a,this):r};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$sce","$animate","$$sanitizeUri",function(a,b,c,n,t,z,u,I,J,D){function L(){try{if(!--qa)throw Z=void 0,ga("infchng",r);u.$apply(function(){for(var a=0,b=Z.length;a<b;++a)Z[a]();Z=void 0})}finally{qa++}}function S(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d< +e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a}function $(a,b,c){na.innerHTML="<span "+b+">";b=na.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function A(a,b){try{a.addClass(b)}catch(c){}}function ba(a,b,c,d,e){a instanceof B||(a=B(a));for(var f=/\S+/,g=0,h=a.length;g<h;g++){var k=a[g];k.nodeType===Ma&&k.nodeValue.match(f)&&Mc(k,a[g]=v.document.createElement("span"))}var l=s(a,b,a,c,d,e);ba.$$addScopeClass(a);var m=null;return function(b, +c,d){qb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var f=d.parentBoundTranscludeFn,g=d.transcludeControllers;d=d.futureParentElement;f&&f.$$boundTransclude&&(f=f.$$boundTransclude);m||(m=(d=d&&d[0])?"foreignobject"!==va(d)&&ma.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==m?B(ca(m,B("<div>").append(a).html())):c?Oa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);ba.$$addScopeInfo(d,b);c&&c(d,b);l&&l(b,d,d,f);return d}}function s(a,b,c,d,e,f){function g(a, +c,d,e){var f,k,l,m,n,t,p;if(r)for(p=Array(c.length),m=0;m<h.length;m+=3)f=h[m],p[f]=c[f];else p=c;m=0;for(n=h.length;m<n;)k=p[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),ba.$$addScopeInfo(B(k),l)):l=a,t=c.transcludeOnThisElement?ka(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?ka(a,b):null,c(f,l,k,d,t)):f&&f(a,k.childNodes,void 0,e)}for(var h=[],k,l,m,n,r,t=0;t<a.length;t++){k=new S;l=x(a[t],[],k,0===t?d:void 0,e);(f=l.length?Ba(l,a[t],k,b,c,null,[],[],f):null)&&f.scope&&ba.$$addScopeClass(k.$$element); +k=f&&f.terminal||!(m=a[t].childNodes)||!m.length?null:s(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(t,f,k),n=!0,r=r||f;f=null}return n?g:null}function ka(a,b,c){function d(e,f,g,h,k){e||(e=a.$new(!1,k),e.$$transcluded=!0);return b(e,f,{parentBoundTranscludeFn:c,transcludeControllers:g,futureParentElement:h})}var e=d.$$slots=T(),f;for(f in b.$$slots)e[f]=b.$$slots[f]?ka(a,b.$$slots[f],c):null;return d}function x(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case 1:la(b, +xa(va(a)),"E",d,e);for(var l,m,n,t=a.attributes,r=0,p=t&&t.length;r<p;r++){var I=!1,D=!1;l=t[r];k=l.name;m=V(l.value);l=xa(k);if(n=ya.test(l))k=k.replace(Vc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(l=l.match(Aa))&&Q(l[1])&&(I=k,D=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(n||!c.hasOwnProperty(l))c[l]=m,Rc(a,l)&&(c[l]=!0);fa(a,b,m,l,n);la(b,l,"A",d,e,I,D)}a=a.className;G(a)&&(a=a.animVal);if(F(a)&&""!==a)for(;k=g.exec(a);)l=xa(k[2]), +la(b,l,"C",d,e)&&(c[l]=V(k[3])),a=a.substr(k.index+k[0].length);break;case Ma:if(11===Ca)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Ma;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);X(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=xa(k[1]),la(b,l,"M",d,e)&&(c[l]=V(k[2]))}catch(J){}}b.sort(Y);return b}function Wc(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ga("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&& +e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function Xc(a,b,c){return function(d,e,f,g,h){e=Wc(e[0],b,c);return a(d,e,f,g,h)}}function Yb(a,b,c,d,e,f){var g;return a?ba(b,c,d,e,f):function(){g||(g=ba(b,c,d,e,f),b=c=f=null);return g.apply(this,arguments)}}function Ba(a,b,d,e,f,g,h,k,l){function m(a,b,c,d){if(a){c&&(a=Xc(a,c,d));a.require=A.require;a.directiveName=M;if(D===A||A.$$isolateScope)a=ha(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Xc(b,c,d)); +b.require=A.require;b.directiveName=M;if(D===A||A.$$isolateScope)b=ha(b,{isolateScope:!0});k.push(b)}}function n(a,c,e,f,g){function l(a,b,c,d){var e;Ya(a)||(d=c,c=b,b=a,a=void 0);H&&(e=u);c||(c=H?z.parent():z);if(d){var f=g.$$slots[d];if(f)return f(a,b,e,c,$);if(y(f))throw ga("noslot",d,wa(z));}else return g(a,b,e,c,$)}var m,t,p,A,w,u,L,z;b===e?(f=d,z=d.$$element):(z=B(e),f=new S(z,d));w=c;D?A=c.$new(!0):r&&(w=c.$parent);g&&(L=l,L.$$boundTransclude=g,L.isSlotFilled=function(a){return!!g.$$slots[a]}); +I&&(u=O(z,f,L,I,A,c,D));D&&(ba.$$addScopeInfo(z,A,!0,!(J&&(J===D||J===D.$$originalDirective))),ba.$$addScopeClass(z,!0),A.$$isolateBindings=D.$$isolateBindings,t=ia(c,f,A,A.$$isolateBindings,D),t.removeWatches&&A.$on("$destroy",t.removeWatches));for(m in u){t=I[m];p=u[m];var Xb=t.$$bindings.bindToController;p.bindingInfo=p.identifier&&Xb?ia(w,f,p.instance,Xb,t):{};var M=p();M!==p.instance&&(p.instance=M,z.data("$"+t.name+"Controller",M),p.bindingInfo.removeWatches&&p.bindingInfo.removeWatches(),p.bindingInfo= +ia(w,f,p.instance,Xb,t))}q(I,function(a,b){var c=a.require;a.bindToController&&!K(c)&&G(c)&&R(u[b].instance,gb(b,c,z,u))});q(u,function(a){var b=a.instance;E(b.$onChanges)&&b.$onChanges(a.bindingInfo.initialChanges);E(b.$onInit)&&b.$onInit();E(b.$onDestroy)&&w.$on("$destroy",function(){b.$onDestroy()})});m=0;for(t=h.length;m<t;m++)p=h[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);var $=c;D&&(D.template||null===D.templateUrl)&&($=A);a&&a($,e.childNodes,void 0,g);for(m= +k.length-1;0<=m;m--)p=k[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);q(u,function(a){a=a.instance;E(a.$postLink)&&a.$postLink()})}l=l||{};for(var t=-Number.MAX_VALUE,r=l.newScopeDirective,I=l.controllerDirectives,D=l.newIsolateScopeDirective,J=l.templateDirective,w=l.nonTlbTranscludeDirective,u=!1,L=!1,H=l.hasElementTranscludeDirective,z=d.$$element=B(b),A,M,$,s=e,Sa,ka=!1,C=!1,v,F=0,Ba=a.length;F<Ba;F++){A=a[F];var P=A.$$start,Q=A.$$end;P&&(z=Wc(b,P,Q));$=void 0; +if(t>A.priority)break;if(v=A.scope)A.templateUrl||(G(v)?(W("new/isolated scope",D||r,A,z),D=A):W("new/isolated scope",D,A,z)),r=r||A;M=A.name;if(!ka&&(A.replace&&(A.templateUrl||A.template)||A.transclude&&!A.$$tlb)){for(v=F+1;ka=a[v++];)if(ka.transclude&&!ka.$$tlb||ka.replace&&(ka.templateUrl||ka.template)){C=!0;break}ka=!0}!A.templateUrl&&A.controller&&(v=A.controller,I=I||T(),W("'"+M+"' controller",I[M],A,z),I[M]=A);if(v=A.transclude)if(u=!0,A.$$tlb||(W("transclusion",w,A,z),w=A),"element"==v)H= +!0,t=A.priority,$=z,z=d.$$element=B(ba.$$createComment(M,d[M])),b=z[0],da(f,za.call($,0),b),$[0].$$parentNode=$[0].parentNode,s=Yb(C,$,e,t,g&&g.name,{nonTlbTranscludeDirective:w});else{var la=T();$=B(Vb(b)).contents();if(G(v)){$=[];var Y=T(),X=T();q(v,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Y[a]=b;la[b]=null;X[b]=c});q(z.contents(),function(a){var b=Y[xa(va(a))];b?(X[b]=!0,la[b]=la[b]||[],la[b].push(a)):$.push(a)});q(X,function(a,b){if(!a)throw ga("reqslot",b);});for(var Z in la)la[Z]&& +(la[Z]=Yb(C,la[Z],e))}z.empty();s=Yb(C,$,e,void 0,void 0,{needsNewScope:A.$$isolateScope||A.$$newScope});s.$$slots=la}if(A.template)if(L=!0,W("template",J,A,z),J=A,v=E(A.template)?A.template(z,d):A.template,v=ta(v),A.replace){g=A;$=Tb.test(v)?Yc(ca(A.templateNamespace,V(v))):[];b=$[0];if(1!=$.length||1!==b.nodeType)throw ga("tplrt",M,"");da(f,z,b);Ba={$attr:{}};v=x(b,[],Ba);var ea=a.splice(F+1,a.length-(F+1));(D||r)&&Zc(v,D,r);a=a.concat(v).concat(ea);U(d,Ba);Ba=a.length}else z.html(v);if(A.templateUrl)L= +!0,W("template",J,A,z),J=A,A.replace&&(g=A),n=aa(a.splice(F,a.length-F),z,d,f,u&&s,h,k,{controllerDirectives:I,newScopeDirective:r!==A&&r,newIsolateScopeDirective:D,templateDirective:J,nonTlbTranscludeDirective:w}),Ba=a.length;else if(A.compile)try{Sa=A.compile(z,d,s),E(Sa)?m(null,Sa,P,Q):Sa&&m(Sa.pre,Sa.post,P,Q)}catch(fa){c(fa,wa(z))}A.terminal&&(n.terminal=!0,t=Math.max(t,A.priority))}n.scope=r&&!0===r.scope;n.transcludeOnThisElement=u;n.templateOnThisElement=L;n.transclude=s;l.hasElementTranscludeDirective= +H;return n}function gb(a,b,c,d){var e;if(F(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&&!f)throw ga("ctreq",b,a);}else if(K(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=gb(a,b[g],c,d);else G(b)&&(e={},q(b,function(b,f){e[f]=gb(a,b,c,d)}));return e||null}function O(a,b,c,d,e,f,g){var h=T(),k;for(k in d){var l=d[k],m={$scope:l===g||l.$$isolateScope?e:f, +$element:a,$attrs:b,$transclude:c},n=l.controller;"@"==n&&(n=b[l.name]);m=z(n,m,!0,l.controllerAs);h[l.name]=m;a.data("$"+l.name+"Controller",m.instance)}return h}function Zc(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Pb(a[d],{$$isolateScope:b,$$newScope:c})}function la(b,f,g,h,k,l,m){if(f===k)return null;k=null;if(e.hasOwnProperty(f)){var n;f=a.get(f+"Directive");for(var t=0,r=f.length;t<r;t++)try{if(n=f[t],(y(h)||h>n.priority)&&-1!=n.restrict.indexOf(g)){l&&(n=Pb(n,{$$start:l,$$end:m}));if(!n.$$bindings){var I= +n,D=n,A=n.name,J={isolateScope:null,bindToController:null};G(D.scope)&&(!0===D.bindToController?(J.bindToController=d(D.scope,A,!0),J.isolateScope={}):J.isolateScope=d(D.scope,A,!1));G(D.bindToController)&&(J.bindToController=d(D.bindToController,A,!0));if(G(J.bindToController)){var w=D.controller,z=D.controllerAs;if(!w)throw ga("noctrl",A);if(!Uc(w,z))throw ga("noident",A);}var u=I.$$bindings=J;G(u.isolateScope)&&(n.$$isolateBindings=u.isolateScope)}b.push(n);k=n}}catch(L){c(L)}}return k}function Q(b){if(e.hasOwnProperty(b))for(var c= +a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=c[d],b.multiElement)return!0;return!1}function U(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,f){"class"==f?(A(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function aa(a,b,c,d,e,f, +g,h){var k=[],l,m,t=b[0],p=a.shift(),r=Pb(p,{templateUrl:null,transclude:null,replace:null,$$originalDirective:p}),I=E(p.templateUrl)?p.templateUrl(b,c):p.templateUrl,D=p.templateNamespace;b.empty();n(I).then(function(n){var J,w;n=ta(n);if(p.replace){n=Tb.test(n)?Yc(ca(D,V(n))):[];J=n[0];if(1!=n.length||1!==J.nodeType)throw ga("tplrt",p.name,I);n={$attr:{}};da(d,b,J);var z=x(J,[],n);G(p.scope)&&Zc(z,!0);a=z.concat(a);U(c,n)}else J=t,b.html(n);a.unshift(r);l=Ba(a,J,c,e,b,p,f,g,h);q(d,function(a,c){a== +J&&(d[c]=b[0])});for(m=s(b[0].childNodes,e);k.length;){n=k.shift();w=k.shift();var u=k.shift(),L=k.shift(),z=b[0];if(!n.$$destroyed){if(w!==t){var S=w.className;h.hasElementTranscludeDirective&&p.replace||(z=Vb(J));da(u,B(w),z);A(B(z),S)}w=l.transcludeOnThisElement?ka(n,l.transclude,L):L;l(m,n,z,d,w)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=ka(b,l.transclude,e)),l(m,b,c,d,a)))}}function Y(a,b){var c=b.priority-a.priority;return 0!== +c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function W(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ga("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,wa(d));}function X(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&ba.$$addBindingClass(a);return function(a,c){var e=c.parent();b||ba.$$addBindingClass(e);ba.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function ca(a,b){a= +P(a||"html");switch(a){case "svg":case "math":var c=v.document.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function ea(a,b){if("srcdoc"==b)return I.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return I.RESOURCE_URL}function fa(a,c,d,e,f){var g=ea(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===va(a))throw ga("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a, +c,h){c=h.$$observers||(h.$$observers=T());if(l.test(e))throw ga("nodomevents");var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function da(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context= +c);break}f&&f.replaceChild(c,d);a=v.document.createDocumentFragment();for(g=0;g<e;g++)a.appendChild(b[g]);B.hasData(d)&&(B.data(c,B.data(d)),B(d).off("$destroy"));B.cleanData(a.querySelectorAll("*"));for(g=1;g<e;g++)delete b[g];b[0]=c;b.length=1}function ha(a,b){return R(function(){return a.apply(null,arguments)},a,b)}function ja(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}function ia(a,c,d,e,f){function g(b,c,e){E(d.$onChanges)&&c!==e&&(Z||(a.$$postDigest(L),Z=[]),m||(m={},Z.push(h)),m[b]&& +(e=m[b].previousValue),m[b]=new Db(e,c))}function h(){d.$onChanges(m);m=void 0}var k=[],l={},m;q(e,function(e,h){var m=e.attrName,n=e.optional,p,r,I,D;switch(e.mode){case "@":n||ua.call(c,m)||(d[h]=c[m]=void 0);c.$observe(m,function(a){if(F(a)||Da(a))g(h,a,d[h]),d[h]=a});c.$$observers[m].$$scope=a;p=c[m];F(p)?d[h]=b(p)(a):Da(p)&&(d[h]=p);l[h]=new Db(Zb,d[h]);break;case "=":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);D=r.literal?pa:function(a,b){return a===b||a!==a&&b!==b}; +I=r.assign||function(){p=d[h]=r(a);throw ga("nonassign",c[m],m,f.name);};p=d[h]=r(a);n=function(b){D(b,d[h])||(D(b,p)?I(a,b=d[h]):d[h]=b);return p=b};n.$stateful=!0;n=e.collection?a.$watchCollection(c[m],n):a.$watch(t(c[m],n),null,r.literal);k.push(n);break;case "<":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);d[h]=r(a);l[h]=new Db(Zb,d[h]);n=a.$watch(r,function(a,b){a===b&&(b=d[h]);g(h,a,b);d[h]=a},r.literal);k.push(n);break;case "&":r=c.hasOwnProperty(m)?t(c[m]):C;if(r=== +C&&n)break;d[h]=function(b){return r(a,b)}}});return{initialChanges:l,removeWatches:k.length&&function(){for(var a=0,b=k.length;a<b;++a)k[a]()}}}var oa=/^\w/,na=v.document.createElement("div"),qa=r,Z;S.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&J.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&J.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=$c(a,b);c&&c.length&&J.addClass(this.$$element,c);(c=$c(b,a))&&c.length&&J.removeClass(this.$$element, +c)},$set:function(a,b,d,e){var f=Rc(this.$$element[0],a),g=ad[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=zc(a,"-"));f=va(this.$$element);if("a"===f&&("href"===a||"xlinkHref"===a)||"img"===f&&"src"===a)this[a]=b=D(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=V(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<k;l++)var m=2*l,f=f+D(V(g[m]),!0),f= +f+(" "+V(g[m+1]));g=V(g[2*l]).split(/\s/);f+=D(V(g[0]),!0);2===g.length&&(f+=" "+V(g[1]));this[a]=b=f}!1!==d&&(null===b||y(b)?this.$$element.removeAttr(e):oa.test(e)?this.$$element.attr(e,b):$(this.$$element[0],e,b));(a=this.$$observers)&&q(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=T()),e=d[a]||(d[a]=[]);e.push(b);u.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||y(c[a])||b(c[a])});return function(){Za(e,b)}}};var ra=b.startSymbol(), +sa=b.endSymbol(),ta="{{"==ra&&"}}"==sa?Xa:function(a){return a.replace(/\{\{/g,ra).replace(/}}/g,sa)},ya=/^ngAttr[A-Z]/,Aa=/^(.+)Start$/;ba.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];K(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:C;ba.$$addBindingClass=m?function(a){A(a,"ng-binding")}:C;ba.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:C;ba.$$addScopeClass=m?function(a,b){A(a,b?"ng-isolate-scope":"ng-scope")}:C;ba.$$createComment= +function(a,b){var c="";m&&(c=" "+(a||"")+": "+(b||"")+" ");return v.document.createComment(c)};return ba}]}function Db(a,b){this.previousValue=a;this.currentValue=b}function xa(a){return cb(a.replace(Vc,""))}function $c(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function Yc(a){a=B(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&Zf.call(a,b,1);return a}function Uc(a, +b){if(b&&F(b))return b;if(F(a)){var d=bd.exec(a);if(d)return d[3]}}function ef(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,c){Qa(b,"controller");G(b)?R(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!G(a.$scope))throw O("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,n,m;h=!0===h;k&&F(k)&&(m=k);if(F(f)){k=f.match(bd);if(!k)throw $f("ctrlfmt",f);n=k[1];m= +m||k[3];f=a.hasOwnProperty(n)?a[n]:Bc(g.$scope,n,!0)||(b?Bc(c,n,!0):void 0);Pa(f,n,!0)}if(h)return h=(K(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),m&&e(g,m,l,n||f.name),R(function(){var a=d.invoke(f,l,g,n);a!==l&&(G(a)||E(a))&&(l=a,m&&e(g,m,l,n||f.name));return l},{instance:l,identifier:m});l=d.instantiate(f,g,n);m&&e(g,m,l,n||f.name);return l}}]}function ff(){this.$get=["$window",function(a){return B(a.document)}]}function gf(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a, +arguments)}}]}function $b(a){return G(a)?fa(a)?a.toISOString():ab(a):a}function mf(){this.$get=function(){return function(a){if(!a)return"";var b=[];pc(a,function(a,c){null===a||y(a)||(K(a)?q(a,function(a){b.push(ja(c)+"="+ja($b(a)))}):b.push(ja(c)+"="+ja($b(a))))});return b.join("&")}}}function nf(){this.$get=function(){return function(a){function b(a,e,f){null===a||y(a)||(K(a)?q(a,function(a,c){b(a,e+"["+(G(a)?c:"")+"]")}):G(a)&&!fa(a)?pc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+ +"="+ja($b(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function ac(a,b){if(F(a)){var d=a.replace(ag,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(cd))||(c=(c=d.match(bg))&&cg[c[0]].test(d));c&&(a=uc(d))}}return a}function dd(a){var b=T(),d;F(a)?q(a.split("\n"),function(a){d=a.indexOf(":");var e=P(V(a.substr(0,d)));a=V(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):G(a)&&q(a,function(a,d){var f=P(d),g=V(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function ed(a){var b; +return function(d){b||(b=dd(a));return d?(d=b[P(d)],void 0===d&&(d=null),d):b}}function fd(a,b,d,c){if(E(c))return c(a,b,d);q(c,function(c){a=c(a,b,d)});return a}function lf(){var a=this.defaults={transformResponse:[ac],transformRequest:[function(a){return G(a)&&"[object File]"!==ma.call(a)&&"[object Blob]"!==ma.call(a)&&"[object FormData]"!==ma.call(a)?ab(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ha(bc),put:ha(bc),patch:ha(bc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN", +paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return x(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return x(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function n(b){function c(a){var b=R({},a);b.data=fd(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};q(a,function(a, +e){E(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!G(b))throw O("$http")("badreq",b);if(!F(b.url))throw O("$http")("badreq",b.url);var f=R({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=R({},b.headers),f,g,h,c=R({},c.common,c[P(b.method)]);a:for(f in c){g=P(f);for(h in d)if(P(h)===g)continue a;d[f]=c[f]}return e(d,ha(b))}(b);f.method=sb(f.method);f.paramSerializer=F(f.paramSerializer)? +l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=fd(b.data,ed(d),void 0,b.transformRequest);y(e)&&q(d,function(a,b){"content-type"===P(b)&&delete d[b]});y(b.withCredentials)&&!y(a.withCredentials)&&(b.withCredentials=a.withCredentials);return m(b,e).then(c,c)},void 0],h=k.when(f);for(q(M,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=g.shift();var n=g.shift(), +h=h.then(b,n)}d?(h.success=function(a){Pa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Pa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=gd("success"),h.error=gd("error"));return h}function m(c,d){function g(a){if(a){var c={};q(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function l(a,c,d,e){function f(){m(c,a,d,e)}L&&(200<=a&&300>a?L.put(A,[a,c,dd(d), +e]):L.remove(A));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function m(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?J.resolve:J.reject)({data:a,status:b,headers:ed(d),config:c,statusText:e})}function u(a){m(a.data,a.status,ha(a.headers()),a.statusText)}function I(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var J=k.defer(),D=J.promise,L,S,M=c.headers,A=r(c.url,c.paramSerializer(c.params));n.pendingRequests.push(c);D.then(I,I);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&& +"JSONP"!==c.method||(L=G(c.cache)?c.cache:G(a.cache)?a.cache:N);L&&(S=L.get(A),x(S)?S&&E(S.then)?S.then(u,u):K(S)?m(S[1],S[0],ha(S[2]),S[3]):m(S,200,{},"OK"):L.put(A,D));y(S)&&((S=hd(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(M[c.xsrfHeaderName||a.xsrfHeaderName]=S),e(c.method,A,d,l,M,c.timeout,c.withCredentials,c.responseType,g(c.eventHandlers),g(c.uploadEventHandlers)));return D}function r(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var N=g("$http");a.paramSerializer= +F(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var M=[];q(c,function(a){M.unshift(F(a)?l.get(a):l.invoke(a))});n.pendingRequests=[];(function(a){q(arguments,function(a){n[a]=function(b,c){return n(R({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){n[a]=function(b,c,d){return n(R({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");n.defaults=a;return n}]}function pf(){this.$get=function(){return function(){return new v.XMLHttpRequest}}} +function of(){this.$get=["$browser","$window","$document","$xhrFactory",function(a,b,d,c){return dg(a,c,a.defer,b.angular.callbacks,d[0])}]}function dg(a,b,d,c,e){function f(a,b,d){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);f=null;var g=-1,N="unknown";a&&("load"!==a.type||c[b].called||(a={type:"error"}),N=a.type,g="error"===a.type?404:200);d&&d(g,N)};f.addEventListener("load", +n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,k,l,n,m,r,N,M,w){function p(){z&&z();u&&u.abort()}function H(b,c,e,f,g){x(J)&&d.cancel(J);z=u=null;b(c,e,f,g);a.$$completeOutstandingRequest(C)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"==P(e)){var t="_"+(c.counter++).toString(36);c[t]=function(a){c[t].data=a;c[t].called=!0};var z=f(h.replace("JSON_CALLBACK","angular.callbacks."+t),t,function(a,b){H(l,a,c[t].data,"",b);c[t]=C})}else{var u=b(e,h); +u.open(e,h,!0);q(n,function(a,b){x(a)&&u.setRequestHeader(b,a)});u.onload=function(){var a=u.statusText||"",b="response"in u?u.response:u.responseText,c=1223===u.status?204:u.status;0===c&&(c=b?200:"file"==ra(h).protocol?404:0);H(l,c,b,u.getAllResponseHeaders(),a)};e=function(){H(l,-1,null,null,"")};u.onerror=e;u.onabort=e;q(M,function(a,b){u.addEventListener(b,a)});q(w,function(a,b){u.upload.addEventListener(b,a)});r&&(u.withCredentials=!0);if(N)try{u.responseType=N}catch(I){if("json"!==N)throw I; +}u.send(y(k)?null:k)}if(0<m)var J=d(p,m);else m&&E(m.then)&&m.then(p)}}function jf(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler","$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(m,a).replace(r,b)}function h(a,b,c,d){var e;return e=a.$watch(function(a){e();return d(a)},b,c)}function k(f,k,m,r){function H(a){try{var b=a;a=m?e.getTrusted(m,b):e.valueOf(b); +var d;if(r&&!x(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=ab(a)}d=a}return d}catch(g){c(Ja.interr(f,g))}}if(!f.length||-1===f.indexOf(a)){var t;k||(k=g(f),t=da(k),t.exp=f,t.expressions=[],t.$$watchDelegate=h);return t}r=!!r;var z,u,I=0,J=[],D=[];t=f.length;for(var L=[],S=[];I<t;)if(-1!=(z=f.indexOf(a,I))&&-1!=(u=f.indexOf(b,z+l)))I!==z&&L.push(g(f.substring(I,z))),I=f.substring(z+l,u),J.push(I),D.push(d(I,H)),I=u+n,S.push(L.length),L.push(""); +else{I!==t&&L.push(g(f.substring(I)));break}m&&1<L.length&&Ja.throwNoconcat(f);if(!k||J.length){var q=function(a){for(var b=0,c=J.length;b<c;b++){if(r&&y(a[b]))return;L[S[b]]=a[b]}return L.join("")};return R(function(a){var b=0,d=J.length,e=Array(d);try{for(;b<d;b++)e[b]=D[b](a);return q(e)}catch(g){c(Ja.interr(f,g))}},{exp:f,expressions:J,$$watchDelegate:function(a,b){var c;return a.$watchGroup(D,function(d,e){var f=q(d);E(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=a.length,n=b.length,m=new RegExp(a.replace(/./g, +f),"g"),r=new RegExp(b.replace(/./g,f),"g");k.startSymbol=function(){return a};k.endSymbol=function(){return b};return k}]}function kf(){this.$get=["$rootScope","$window","$q","$$q","$browser",function(a,b,d,c,e){function f(f,k,l,n){function m(){r?f.apply(null,N):f(p)}var r=4<arguments.length,N=r?za.call(arguments,4):[],q=b.setInterval,w=b.clearInterval,p=0,H=x(n)&&!n,t=(H?c:d).defer(),z=t.promise;l=x(l)?l:0;z.$$intervalId=q(function(){H?e.defer(m):a.$evalAsync(m);t.notify(p++);0<l&&p>=l&&(t.resolve(p), +w(z.$$intervalId),delete g[z.$$intervalId]);H||a.$apply()},k);g[z.$$intervalId]=t;return z}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete g[a.$$intervalId],!0):!1};return f}]}function cc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=ob(a[b]);return a.join("/")}function id(a,b){var d=ra(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=X(d.port)||eg[d.protocol]||null}function jd(a,b){var d="/"!==a.charAt(0); +d&&(a="/"+a);var c=ra(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function na(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ia(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function hb(a){return a.replace(/(#.+)|#$/,"$1")}function dc(a,b,d){this.$$html5=!0;d=d||"";id(a,this);this.$$parse=function(a){var d=na(b, +a);if(!F(d))throw Eb("ipthprfx",a,b);jd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Rb(this.$$search),d=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;x(f=na(a,c))?(g=f,g=x(f=na(d,f))?b+(na("/",f)||f):a+g):x(f=na(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function ec(a,b,d){id(a,this); +this.$$parse=function(c){var e=na(a,c)||na(b,c),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(a=c,this.replace())):(f=na(d,e),y(f)&&(f=e));jd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl= +function(b,d){return Ia(a)==Ia(b)?(this.$$parse(b),!0):!1}}function kd(a,b,d){this.$$html5=!0;ec.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ia(c)?f=c:(g=na(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Fb(a){return function(){return this[a]}}function ld(a, +b){return function(d){if(y(d))return this[a];this[a]=b(d);this.$$compose();return this}}function qf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return x(b)?(a=b,this):a};this.html5Mode=function(a){return Da(a)?(b.enabled=a,this):G(a)?(Da(a.enabled)&&(b.enabled=a.enabled),Da(a.requireBase)&&(b.requireBase=a.requireBase),Da(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d, +c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,n;n=c.baseHref();var m=c.url(),r;if(b.enabled){if(!n&&b.requireBase)throw Eb("nobase");r=m.substring(0,m.indexOf("/",m.indexOf("//")+2))+(n||"/");n=e.history?dc:kd}else r=Ia(m),n=ec;var N=r.substr(0,Ia(r).lastIndexOf("/")+1);l=new n(r,N,"#"+a);l.$$parseLinkUrl(m,m);l.$$state=c.state(); +var q=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=B(a.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");G(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=ra(h.animVal).href);q.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]= +!0))}});hb(l.absUrl())!=hb(m)&&c.url(l.absUrl(),!0);var w=!0;c.onUrlChange(function(a,b){y(na(N,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=hb(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(w=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=hb(c.url()),b=hb(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(w|| +m)w=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function rf(){var a=!0,b=this;this.debugEnabled=function(b){return x(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&& +(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||C;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];q(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Ta(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"=== +a||"__proto__"===a)throw ca("isecfld",b);return a}function fg(a){return a+""}function sa(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a.window===a)throw ca("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw ca("isecdom",b);if(a===Object)throw ca("isecobj",b);}return a}function md(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a===gg||a===hg||a===ig)throw ca("isecff",b);}}function Gb(a,b){if(a&&(a===(0).constructor||a===(!1).constructor||a==="".constructor|| +a==={}.constructor||a===[].constructor||a===Function.constructor))throw ca("isecaf",b);}function jg(a,b){return"undefined"!==typeof a?a:b}function nd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function aa(a,b){var d,c;switch(a.type){case s.Program:d=!0;q(a.body,function(a){aa(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:aa(a.argument,b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch; +break;case s.BinaryExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:aa(a.test,b);aa(a.alternate,b);aa(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant= +!1;a.toWatch=[a];break;case s.MemberExpression:aa(a.object,b);a.computed&&aa(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];q(a.arguments,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant; +a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];q(a.elements,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];q(a.properties,function(a){aa(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1;a.toWatch=[];break;case s.LocalsExpression:a.constant=!1,a.toWatch=[]}}function od(a){if(1==a.length){a=a[0].expression; +var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function pd(a){return a.type===s.Identifier||a.type===s.MemberExpression}function qd(a){if(1===a.body.length&&pd(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function rd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function sd(a, +b){this.astBuilder=a;this.$filter=b}function td(a,b){this.astBuilder=a;this.$filter=b}function Hb(a){return"constructor"==a}function fc(a){return E(a.valueOf)?a.valueOf():kg.call(a)}function sf(){var a=T(),b=T(),d={"true":!0,"false":!1,"null":null,undefined:void 0},c,e;this.addLiteral=function(a,b){d[a]=b};this.setIdentifierFns=function(a,b){c=a;e=b;return this};this.$get=["$filter",function(f){function g(c,d,e){var g,k,D;e=e||H;switch(typeof c){case "string":D=c=c.trim();var q=e?b:a;g=q[D];if(!g){":"=== +c.charAt(0)&&":"===c.charAt(1)&&(k=!0,c=c.substring(2));g=e?p:w;var S=new gc(g);g=(new hc(S,f,g)).parse(c);g.constant?g.$$watchDelegate=r:k?g.$$watchDelegate=g.literal?m:n:g.inputs&&(g.$$watchDelegate=l);e&&(g=h(g));q[D]=g}return N(g,d);case "function":return N(c,d);default:return N(C,d)}}function h(a){function b(c,d,e,f){var g=H;H=!0;try{return a(c,d,e,f)}finally{H=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=h(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&& +c<a.inputs.length;++c)a.inputs[c]=h(a.inputs[c]);b.inputs=a.inputs;return b}function k(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=fc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function l(a,b,c,d,e){var f=d.inputs,g;if(1===f.length){var h=k,f=f[0];return a.$watch(function(a){var b=f(a);k(b,h)||(g=d(a,void 0,void 0,[b]),h=b&&fc(b));return g},b,c,e)}for(var l=[],m=[],n=0,r=f.length;n<r;n++)l[n]=k,m[n]=null;return a.$watch(function(a){for(var b=!1,c=0,e=f.length;c<e;c++){var h=f[c](a); +if(b||(b=!k(h,l[c])))m[c]=h,l[c]=h&&fc(h)}b&&(g=d(a,void 0,void 0,m));return g},b,c,e)}function n(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;E(b)&&b.apply(this,arguments);x(a)&&d.$$postDigest(function(){x(f)&&e()})},c)}function m(a,b,c,d){function e(a){var b=!0;q(a,function(a){x(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;E(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function r(a,b,c,d){var e; +return e=a.$watch(function(a){e();return d(a)},b,c)}function N(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==m&&c!==n?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return x(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==l?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=l,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var M=Ea().noUnsafeEval,w={csp:M,expensiveChecks:!1,literals:qa(d),isIdentifierStart:E(c)&&c, +isIdentifierContinue:E(e)&&e},p={csp:M,expensiveChecks:!0,literals:qa(d),isIdentifierStart:E(c)&&c,isIdentifierContinue:E(e)&&e},H=!1;g.$$runningExpensiveChecks=function(){return H};return g}]}function uf(){this.$get=["$rootScope","$exceptionHandler",function(a,b){return ud(function(b){a.$evalAsync(b)},b)}]}function vf(){this.$get=["$browser","$exceptionHandler",function(a,b){return ud(function(b){a.defer(b)},b)}]}function ud(a,b){function d(){this.$$state={status:0}}function c(a,b){return function(c){b.call(a, +c)}}function e(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,d,e;e=c.pending;c.processScheduled=!1;c.pending=void 0;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];try{E(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function f(){this.promise=new d}var g=O("$q",TypeError);R(d.prototype,{then:function(a,b,c){if(y(a)&&y(b)&&y(c))return this;var d=new f;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d, +a,b,c]);0<this.$$state.status&&e(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}});R(f.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(g("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){function d(a){k||(k=!0,h.$$resolve(a))}function f(a){k||(k=!0,h.$$reject(a))}var g,h=this,k=!1;try{if(G(a)||E(a))g=a&&a.then;E(g)? +(this.promise.$$state.status=-1,g.call(a,d,f,c(this,this.notify))):(this.promise.$$state.value=a,this.promise.$$state.status=1,e(this.promise.$$state))}catch(l){f(l),b(l)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;e(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0]; +a=d[f][3];try{e.notify(E(a)?a(c):c)}catch(h){b(h)}}})}});var h=function(a,b){var c=new f;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{E(c)&&(d=c())}catch(e){return h(e,!1)}return d&&E(d.then)?d.then(function(){return h(a,b)},function(a){return h(a,!1)}):h(a,b)},l=function(a,b,c,d){var e=new f;e.resolve(a);return e.promise.then(b,c,d)},n=function(a){if(!E(a))throw g("norslvr",a);var b=new f;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};n.prototype= +d.prototype;n.defer=function(){var a=new f;a.resolve=c(a,a.resolve);a.reject=c(a,a.reject);a.notify=c(a,a.notify);return a};n.reject=function(a){var b=new f;b.reject(a);return b.promise};n.when=l;n.resolve=l;n.all=function(a){var b=new f,c=0,d=K(a)?[]:{};q(a,function(a,e){c++;l(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return n}function Ef(){this.$get=["$window","$timeout",function(a, +b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function tf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope= +null}b.prototype=a;return b}var b=10,d=O("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$exceptionHandler","$parse","$browser",function(f,g,h){function k(a){a.currentScope.$$destroyed=!0}function l(a){9===Ca&&(a.$$childHead&&l(a.$$childHead),a.$$nextSibling&&l(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function n(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling= +this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function m(a){if(H.$$phase)throw d("inprog",H.$$phase);H.$$phase=a}function r(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function N(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function w(){for(;u.length;)try{u.shift()()}catch(a){f(a)}e= +null}function p(){null===e&&(e=h.defer(function(){H.$apply(w)}))}n.prototype={constructor:n,$new:function(b,c){var d;c=c||this;b?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,d,e){var f=g(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f, +a);var h=this,k=h.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;E(b)||(l.fn=C);k||(k=h.$$watchers=[]);k.unshift(l);r(this,1);return function(){0<=Za(k,l)&&r(h,-1);c=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});q(a,function(a, +b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!y(e)){if(G(e))if(ya(e))for(f!==m&&(f=m,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==r&&(f=r={},t=0,l++);a=0;for(b in e)ua.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(t++,f[b]=g,l++));if(t> +a)for(b in l++,f)ua.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1<b.length,l=0,n=g(a,c),m=[],r={},p=!0,t=0;return this.$watch(n,function(){p?(p=!1,b(e,e,d)):b(e,h,d);if(k)if(G(e))if(ya(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h={},e)ua.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var a,g,k,l,n,r,p,q,N=b,u,x=[],y,v;m("$digest");h.$$checkUrlChange();this===H&&null!==e&&(h.defer.cancel(e),w());c=null;do{q=!1; +for(u=this;t.length;){try{v=t.shift(),v.scope.$eval(v.expression,v.locals)}catch(C){f(C)}c=null}a:do{if(r=u.$$watchers)for(p=r.length;p--;)try{if(a=r[p])if(n=a.get,(g=n(u))!==(k=a.last)&&!(a.eq?pa(g,k):"number"===typeof g&&"number"===typeof k&&isNaN(g)&&isNaN(k)))q=!0,c=a,a.last=a.eq?qa(g,null):g,l=a.fn,l(g,k===s?g:k,u),5>N&&(y=4-N,x[y]||(x[y]=[]),x[y].push({msg:E(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){q=!1;break a}}catch(F){f(F)}if(!(r=u.$$watchersCount&& +u.$$childHead||u!==this&&u.$$nextSibling))for(;u!==this&&!(r=u.$$nextSibling);)u=u.$parent}while(u=r);if((q||t.length)&&!N--)throw H.$$phase=null,d("infdig",b,x);}while(q||t.length);for(H.$$phase=null;z.length;)try{z.shift()()}catch(B){f(B)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===H&&h.$$applicationDestroyed();r(this,-this.$$watchersCount);for(var b in this.$$listenerCount)N(this,this.$$listenerCount[b],b);a&&a.$$childHead== +this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=C;this.$on=this.$watch=this.$watchGroup=function(){return C};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){H.$$phase|| +t.length||h.defer(function(){t.length&&H.$digest()});t.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){z.push(a)},$apply:function(a){try{m("$apply");try{return this.$eval(a)}finally{H.$$phase=null}}catch(b){f(b)}finally{try{H.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&u.push(b);a=g(a);p()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]= +0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,N(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=$a([h],arguments,1),l,n;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(n=d.length;l<n;l++)if(d[l])try{d[l].apply(null,k)}catch(m){f(m)}else d.splice(l,1),l--,n--;if(g)return h.currentScope=null,h;e=e.$parent}while(e); +h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var g=$a([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){f(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope= +null;return e}};var H=new n,t=H.$$asyncQueue=[],z=H.$$postDigestQueue=[],u=H.$$applyAsyncQueue=[];return H}]}function me(){var a=/^\s*(https?|ftp|mailto|tel|file):/,b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return x(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=ra(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function lg(a){if("self"===a)return a; +if(F(a)){if(-1<a.indexOf("***"))throw ta("iwcard",a);a=vd(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+a+"$")}if(Wa(a))return new RegExp("^"+a.source+"$");throw ta("imatcher");}function wd(a){var b=[];x(a)&&q(a,function(a){b.push(lg(a))});return b}function xf(){this.SCE_CONTEXTS=oa;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=wd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=wd(a));return b};this.$get=["$injector", +function(d){function c(a,b){return"self"===a?hd(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw ta("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[oa.HTML]=e(g);h[oa.CSS]=e(g);h[oa.URL]=e(g);h[oa.JS]=e(g);h[oa.RESOURCE_URL]= +e(h[oa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw ta("icontext",a,b);if(null===b||y(b)||""===b)return b;if("string"!==typeof b)throw ta("itype",a);return new c(b)},getTrusted:function(d,e){if(null===e||y(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===oa.RESOURCE_URL){var g=ra(e.toString()),m,r,q=!1;m=0;for(r=a.length;m<r;m++)if(c(a[m],g)){q=!0;break}if(q)for(m=0,r=b.length;m<r;m++)if(c(b[m], +g)){q=!1;break}if(q)return e;throw ta("insecurl",e.toString());}if(d===oa.HTML)return f(e);throw ta("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function wf(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ca)throw ta("iequirks");var c=ha(oa);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b}, +c.valueOf=Xa);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;q(oa,function(a,b){var d=P(b);c[cb("parse_as_"+d)]=function(b){return e(a,b)};c[cb("get_trusted_"+d)]=function(b){return f(a,b)};c[cb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function yf(){this.$get=["$window","$document",function(a,b){var d={},c=!(a.chrome&&a.chrome.app&&a.chrome.app.runtime)&&a.history&&a.history.pushState, +e=X((/android (\d+)/.exec(P((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},h,k=/^(Moz|webkit|ms)(?=[A-Z])/,l=g.body&&g.body.style,n=!1,m=!1;if(l){for(var r in l)if(n=k.exec(r)){h=n[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");n=!!("transition"in l||h+"Transition"in l);m=!!("animation"in l||h+"Animation"in l);!e||n&&m||(n=F(l.webkitTransition),m=F(l.webkitAnimation))}return{history:!(!c||4>e||f),hasEvent:function(a){if("input"=== +a&&11>=Ca)return!1;if(y(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ea(),vendorPrefix:h,transitions:n,animations:m,android:e}}]}function Af(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q","$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;F(g)&&b.get(g)||(g=e.getTrustedResourceUrl(g));var k=d.defaults&&d.defaults.transformResponse;K(k)?k=k.filter(function(a){return a!==ac}):k===ac&&(k=null);return d.get(g, +R({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw mg("tpload",g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function Bf(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];q(a,function(a){var c=ea.element(a).data("$binding");c&&q(c,function(c){d?(new RegExp("(^|\\s)"+ +vd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function Cf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler", +function(a,b,d,c,e){function f(f,k,l){E(f)||(l=k,k=f,f=C);var n=za.call(arguments,3),m=x(l)&&!l,r=(m?c:d).defer(),q=r.promise,s;s=b.defer(function(){try{r.resolve(f.apply(null,n))}catch(b){r.reject(b),e(b)}finally{delete g[q.$$timeoutId]}m||a.$apply()},k);q.$$timeoutId=s;g[s]=r;return q}var g={};f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function ra(a){Ca&&(Y.setAttribute("href",a),a= +Y.href);Y.setAttribute("href",a);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:"/"+Y.pathname}}function hd(a){a=F(a)?ra(a):a;return a.protocol===xd.protocol&&a.host===xd.host}function Df(){this.$get=da(v)}function yd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{}, +c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),y(c[l])&&(c[l]=b(g.substring(k+1))));return c}}function Hf(){this.$get=yd}function Jc(a){function b(d,c){if(G(d)){var e={};q(d,function(a,c){e[c]=b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",zd);b("date",Ad);b("filter",ng); +b("json",og);b("limitTo",pg);b("lowercase",qg);b("number",Bd);b("orderBy",Cd);b("uppercase",rg)}function ng(){return function(a,b,d){if(!ya(a)){if(null==a)return a;throw O("filter")("notarray",a);}var c;switch(ic(b)){case "function":break;case "boolean":case "null":case "number":case "string":c=!0;case "object":b=sg(b,d,c);break;default:return a}return Array.prototype.filter.call(a,b)}}function sg(a,b,d){var c=G(a)&&"$"in a;!0===b?b=pa:E(b)||(b=function(a,b){if(y(a))return!1;if(null===a||null===b)return a=== +b;if(G(b)||G(a)&&!rc(a))return!1;a=P(""+a);b=P(""+b);return-1!==a.indexOf(b)});return function(e){return c&&!G(e)?Ka(e,a.$,b,!1):Ka(e,a,b,d)}}function Ka(a,b,d,c,e){var f=ic(a),g=ic(b);if("string"===g&&"!"===b.charAt(0))return!Ka(a,b.substring(1),d,c);if(K(a))return a.some(function(a){return Ka(a,b,d,c)});switch(f){case "object":var h;if(c){for(h in a)if("$"!==h.charAt(0)&&Ka(a[h],b,d,!0))return!0;return e?!1:Ka(a,b,d,!1)}if("object"===g){for(h in b)if(e=b[h],!E(e)&&!y(e)&&(f="$"===h,!Ka(f?a:a[h], +e,d,f,f)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function ic(a){return null===a?"null":typeof a}function zd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){y(c)&&(c=b.CURRENCY_SYM);y(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Dd(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,e).replace(/\u00A4/g,c)}}function Bd(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Dd(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function tg(a){var b=0,d,c,e,f,g;-1< +(c=a.indexOf(Ed))&&(a=a.replace(Ed,""));0<(e=a.search(/e/i))?(0>c&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==jc;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==jc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Fd&&(d=d.splice(0,Fd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function ug(a,b,d,c){var e=a.d,f=e.length-a.i;b=y(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0<d){e.splice(Math.max(a.i,d));for(var g=d;g<e.length;g++)e[g]=0}else for(f= +Math.max(0,f),a.i=1,e.length=Math.max(1,d=b+1),e[0]=0,g=1;g<d;g++)e[g]=0;if(5<=c)if(0>d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;f<Math.max(0,b);f++)e.push(0);if(b=e.reduceRight(function(a,b,c,d){b+=a;d[c]=b%10;return Math.floor(b/10)},0))e.unshift(b),a.i++}function Dd(a,b,d,c,e){if(!F(a)&&!Q(a)||isNaN(a))return"";var f=!isFinite(a),g=!1,h=Math.abs(a)+"",k="";if(f)k="\u221e";else{g=tg(h);ug(g,e,b.minFrac,b.maxFrac);k=g.d;h=g.i;e=g.e;f=[];for(g=k.reduce(function(a, +b){return a&&!b},!0);0>h;)k.unshift(0),h++;0<h?f=k.splice(h):(f=k,k=[0]);h=[];for(k.length>=b.lgSize&&h.unshift(k.splice(-b.lgSize).join(""));k.length>b.gSize;)h.unshift(k.splice(-b.gSize).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Ib(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length<b;)a=jc+a;d&&(a=a.substr(a.length-b));return e+a}function W(a,b,d,c,e){d= +d||0;return function(f){f=f["get"+a]();if(0<d||f>-d)f+=d;0===f&&-12==d&&(f=12);return Ib(f,b,c,e)}}function ib(a,b,d){return function(c,e){var f=c["get"+a](),g=sb((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Gd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Hd(a){return function(b){var d=Gd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ib(b,a)}}function kc(a,b){return 0>=a.getFullYear()? +b.ERAS[0]:b.ERAS[1]}function Ad(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=X(b[9]+b[10]),g=X(b[9]+b[11]));h.call(a,X(b[1]),X(b[2])-1,X(b[3]));f=X(b[4]||0)-f;g=X(b[5]||0)-g;h=X(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h= +[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;F(c)&&(c=vg.test(c)?X(c):b(c));Q(c)&&(c=new Date(c));if(!fa(c)||!isFinite(c.getTime()))return c;for(;d;)(l=wg.exec(d))?(h=$a(h,l,1),d=h.pop()):(h.push(d),d=null);var n=c.getTimezoneOffset();f&&(n=vc(f,n),c=Qb(c,f,!0));q(h,function(b){k=xg[b];g+=k?k(c,a.DATETIME_FORMATS,n):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function og(){return function(a,b){y(b)&&(b=2);return ab(a,b)}}function pg(){return function(a,b,d){b=Infinity=== +Math.abs(Number(b))?Number(b):X(b);if(isNaN(b))return a;Q(a)&&(a=a.toString());if(!K(a)&&!F(a))return a;d=!d||isNaN(d)?0:X(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Cd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Xa;if(E(b))h=b;else if(F(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h, +descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(null==a)return a;if(!ya(a))throw O("orderBy")("notarray",a);K(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"=== +c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(rc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],q=0;c.type===f.type?c.value!==f.value&&(q=c.value<f.value?-1:1):q=c.type<f.type?-1:1;if(c=q*g[d].descending)break}return c});return a=a.map(function(a){return a.value})}}function La(a){E(a)&&(a={link:a});a.restrict=a.restrict||"AC";return da(a)}function Id(a, +b,d,c,e){var f=this,g=[];f.$error={};f.$$success={};f.$pending=void 0;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Jb;f.$rollbackViewValue=function(){q(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){q(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Qa(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c]; +f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];q(f.$pending,function(b,c){f.$setValidity(c,null,a)});q(f.$error,function(b,c){f.$setValidity(c,null,a)});q(f.$$success,function(b,c){f.$setValidity(c,null,a)});Za(g,a);a.$$parentForm=Jb};Jd({ctrl:this,$element:a,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Za(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Ua); +c.addClass(a,Kb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};f.$setPristine=function(){c.setClass(a,Ua,Kb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;q(g,function(a){a.$setPristine()})};f.$setUntouched=function(){q(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function lc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function jb(a,b,d,c,e,f){var g=P(b[0].type); +if(!e.android){var h=!1;b.on("compositionstart",function(){h=!0});b.on("compositionend",function(){h=!1;l()})}var k,l=function(a){k&&(f.defer.cancel(k),k=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=V(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",l);else{var n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15< +b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",n)}b.on("change",l);if(Kd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Lb(a,b){return function(d,c){var e,f;if(fa(d))return d;if(F(d)){'"'==d.charAt(0)&& +'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(yg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},q(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(a,b,d,c){return function(e,f,g,h,k,l,n){function m(a){return a&& +!(a.getTime&&a.getTime()!==a.getTime())}function r(a){return x(a)&&!fa(a)?d(a)||void 0:a}Ld(e,f,g,h);jb(e,f,g,h,k,l);var q=h&&h.$options&&h.$options.timezone,s;h.$$parserName=a;h.$parsers.push(function(a){if(h.$isEmpty(a))return null;if(b.test(a))return a=d(a,s),q&&(a=Qb(a,q)),a});h.$formatters.push(function(a){if(a&&!fa(a))throw lb("datefmt",a);if(m(a))return(s=a)&&q&&(s=Qb(s,q,!0)),n("date")(a,c,q);s=null;return""});if(x(g.min)||g.ngMin){var w;h.$validators.min=function(a){return!m(a)||y(w)||d(a)>= +w};g.$observe("min",function(a){w=r(a);h.$validate()})}if(x(g.max)||g.ngMax){var p;h.$validators.max=function(a){return!m(a)||y(p)||d(a)<=p};g.$observe("max",function(a){p=r(a);h.$validate()})}}}function Ld(a,b,d,c){(c.$$hasNativeValidators=G(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?void 0:a})}function Md(a,b,d,c,e){if(x(c)){a=a(c);if(!a.constant)throw lb("constexpr",d,c);return a(b)}return e}function mc(a,b){a="ngClass"+a;return["$animate", +function(d){function c(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){var b=[];return K(a)?(q(a,function(a){b=b.concat(e(a))}),b):F(a)?a.split(" "):G(a)?(q(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,g,h){function k(a){a=l(a,1);h.$addClass(a)}function l(a,b){var c=g.data("$classCounts")||T(),d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts", +c);return d.join(" ")}function n(a,b){var e=c(b,a),f=c(a,b),e=l(e,1),f=l(f,-1);e&&e.length&&d.addClass(g,e);f&&f.length&&d.removeClass(g,f)}function m(a){if(!0===b||f.$index%2===b){var c=e(a||[]);if(!r)k(c);else if(!pa(a,r)){var d=e(r);n(d,c)}}r=K(a)?a.map(function(a){return ha(a)}):ha(a)}var r;f.$watch(h[a],m,!0);h.$observe("class",function(b){m(f.$eval(h[a]))});"ngClass"!==a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var m=e(f.$eval(h[a]));g===b?k(m):(g=l(m,-1),h.$removeClass(g))}})}}}]} +function Jd(a){function b(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+zc(a,"-"):"";b(mb+a,!0===c);b(Nd+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Nd]=!(f[mb]=e.hasClass(mb));c.$setValidity=function(a,e,f){y(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&h(c.$pending,a,f),Od(c.$pending)&&(c.$pending=void 0));Da(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error, +a,f),h(c.$$success,a,f));c.$pending?(b(Pd,!0),c.$valid=c.$invalid=void 0,d("",null)):(b(Pd,!1),c.$valid=Od(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?void 0:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Od(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var zg=/^\/(.+)\/([a-z]*)$/,ua=Object.prototype.hasOwnProperty,P=function(a){return F(a)?a.toLowerCase():a},sb=function(a){return F(a)?a.toUpperCase():a},Ca, +B,Z,za=[].slice,Zf=[].splice,Ag=[].push,ma=Object.prototype.toString,sc=Object.getPrototypeOf,Aa=O("ng"),ea=v.angular||(v.angular={}),Sb,nb=0;Ca=v.document.documentMode;C.$inject=[];Xa.$inject=[];var K=Array.isArray,$d=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,V=function(a){return F(a)?a.trim():a},vd=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Ea=function(){if(!x(Ea.rules)){var a=v.document.querySelector("[ng-csp]")|| +v.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ea.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ea;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ea.rules},pb=function(){if(x(pb.name_))return pb.name_;var a,b,d=Na.length,c,e;for(b=0;b<d;++b)if(c=Na[b],a=v.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+ +"jq");break}return pb.name_=e},ce=/:/g,Na=["ng-","data-ng-","ng:","x-ng-"],he=/[A-Z]/g,Ac=!1,Ma=3,le={full:"1.5.5",major:1,minor:5,dot:5,codeName:"material-conspiration"};U.expando="ng339";var eb=U.cache={},Nf=1;U._data=function(a){return this.cache[a[this.expando]]||{}};var If=/([\:\-\_]+(.))/g,Jf=/^moz([A-Z])/,wb={mouseleave:"mouseout",mouseenter:"mouseover"},Ub=O("jqLite"),Mf=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Kf=/<([\w:-]+)/,Lf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, +ia={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option;ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead;ia.th=ia.td;var Sf=v.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Oa=U.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"=== +v.document.readyState?v.setTimeout(b):(this.on("DOMContentLoaded",b),U(v).on("load",b))},toString:function(){var a=[];q(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?B(this[a]):B(this[this.length+a])},length:0,push:Ag,sort:[].sort,splice:[].splice},Cb={};q("multiple selected checked disabled readOnly required open".split(" "),function(a){Cb[P(a)]=a});var Sc={};q("input select option textarea button form details".split(" "),function(a){Sc[a]=!0});var ad={ngMinlength:"minlength", +ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};q({data:Wb,removeData:db,hasData:function(a){for(var b in eb[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)db(a[b])}},function(a,b){U[b]=a});q({data:Wb,inheritedData:Ab,scope:function(a){return B.data(a,"$scope")||Ab(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return B.data(a,"$isolateScope")||B.data(a,"$isolateScopeNoTemplate")},controller:Pc,injector:function(a){return Ab(a, +"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:xb,css:function(a,b,d){b=cb(b);if(x(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Ma&&2!==c&&8!==c)if(c=P(b),Cb[c])if(x(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||C).specified?c:void 0;else if(x(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?void 0:a},prop:function(a,b,d){if(x(d))a[b]= +d;else return a[b]},text:function(){function a(a,d){if(y(d)){var c=a.nodeType;return 1===c||c===Ma?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(y(b)){if(a.multiple&&"select"===va(a)){var d=[];q(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(y(b))return a.innerHTML;ub(a,!0);a.innerHTML=b},empty:Qc},function(a,b){U.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Qc&&y(2==a.length&& +a!==xb&&a!==Pc?b:c)){if(G(b)){for(e=0;e<g;e++)if(a===Wb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=y(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});q({removeData:db,on:function(a,b,d,c){if(x(c))throw Ub("onargs");if(Kc(a)){c=vb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Pf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper= +c,"$destroy"===b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],wb[b]?(h(wb[b],Rf),h(b,void 0,!0)):h(b)}},off:Oc,one:function(a,b,d){a=B(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;ub(a);q(new U(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];q(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a, +b){var d=a.nodeType;if(1===d||11===d){b=new U(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;q(new U(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){Mc(a,B(b).eq(0).clone()[0])},remove:Bb,detach:function(a){Bb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new U(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:zb,removeClass:yb,toggleClass:function(a,b,d){b&&q(b.split(" "), +function(b){var e=d;y(e)&&(e=!xb(a,b));(e?zb:yb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Vb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=vb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped= +!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:C,type:f,target:a},b.type&&(c=R(c,b)),b=ha(g),e=d?[c].concat(d):[c],q(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){U.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)y(f)?(f=a(this[g],b,c,e),x(f)&&(f=B(f))):Nc(f,a(this[g],b,c,e));return x(f)?f:this};U.prototype.bind=U.prototype.on;U.prototype.unbind=U.prototype.off});Ra.prototype={put:function(a, +b){this[Fa(a,this.nextUid)]=b},get:function(a){return this[Fa(a,this.nextUid)]},remove:function(a){var b=this[a=Fa(a,this.nextUid)];delete this[a];return b}};var Gf=[function(){this.$get=[function(){return Ra}]}],Uf=/^([^\(]+?)=>/,Vf=/^[^\(]*\(\s*([^\)]*)\)/m,Bg=/,/,Cg=/^\s*(_?)(\S+?)\1\s*$/,Tf=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ga=O("$injector");bb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw F(d)&&d||(d=a.name||Wf(a)),Ga("strictdi",d); +b=Tc(a);q(b[1].split(Bg),function(a){a.replace(Cg,function(a,b,d){c.push(d)})})}a.$inject=c}}else K(a)?(b=a.length-1,Pa(a[b],"fn"),c=a.slice(0,b)):Pa(a,"fn",!0);return c};var Qd=O("$animate"),Ze=function(){this.$get=C},$e=function(){var a=new Ra,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=F(b)?b.split(" "):K(b)?b:[],q(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){q(b,function(b){var c=a.get(b);if(c){var d=Xf(b.attr("class")),e="",f="";q(c, +function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});q(b,function(a){e&&zb(a,e);f&&yb(a,f)});a.remove(b)}});b.length=0}return{enabled:C,on:C,off:C,pin:C,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},Xe=["$provide",function(a){var b=this;this.$$registeredAnimations= +Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Qd("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Qd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k= +d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ha(h))},move:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ha(h))},leave:function(b,c){return a.push(b,"leave",Ha(c),function(){b.remove()})},addClass:function(b, +c,g){g=Ha(g);g.addClass=fb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ha(g);g.removeClass=fb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ha(h);h.addClass=fb(h.addClass,c);h.removeClass=fb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ha(k);k.from=k.from?R(k.from,c):c;k.to=k.to?R(k.to,g):g;k.tempClasses=fb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],bf=function(){this.$get= +["$$rAF",function(a){function b(b){d.push(b);1<d.length||a(function(){for(var a=0;a<d.length;a++)d[a]();d=[]})}var d=[];return function(){var a=!1;b(function(){a=!0});return function(d){a?d():b(d)}}}]},af=function(){this.$get=["$q","$sniffer","$$animateAsyncRun","$document","$timeout",function(a,b,d,c,e){function f(a){this.setHost(a);var b=d();this._doneCallbacks=[];this._tick=function(a){var d=c[0];d&&d.hidden?e(a,0,!1):b(a)};this._state=0}f.chain=function(a,b){function c(){if(d===a.length)b(!0); +else a[d](function(a){!1===a?b(!1):(d++,c())})}var d=0;c()};f.all=function(a,b){function c(f){e=e&&f;++d===a.length&&b(e)}var d=0,e=!0;q(a,function(a){a.done(c)})};f.prototype={setHost:function(a){this.host=a||{}},done:function(a){2===this._state?a():this._doneCallbacks.push(a)},progress:C,getPromise:function(){if(!this.promise){var b=this;this.promise=a(function(a,c){b.done(function(b){!1===b?c():a()})})}return this.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)}, +"finally":function(a){return this.getPromise()["finally"](a)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end();this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel();this._resolve(!1)},complete:function(a){var b=this;0===b._state&&(b._state=1,b._tick(function(){b._resolve(a)}))},_resolve:function(a){2!==this._state&&(q(this._doneCallbacks,function(b){b(a)}),this._doneCallbacks.length= +0,this._state=2)}};return f}]},Ye=function(){this.$get=["$$rAF","$q","$$AnimateRunner",function(a,b,d){return function(b,e){function f(){a(function(){g.addClass&&(b.addClass(g.addClass),g.addClass=null);g.removeClass&&(b.removeClass(g.removeClass),g.removeClass=null);g.to&&(b.css(g.to),g.to=null);h||k.complete();h=!0});return k}var g=e||{};g.$$prepared||(g=qa(g));g.cleanupStyles&&(g.from=g.to=null);g.from&&(b.css(g.from),g.from=null);var h,k=new d;return{start:f,end:f}}}]},ga=O("$compile"),Zb=new function(){}; +Cc.$inject=["$provide","$$sanitizeUriProvider"];Db.prototype.isFirstChange=function(){return this.previousValue===Zb};var Vc=/^((?:x|data)[\:\-_])/i,$f=O("$controller"),bd=/^(\S+)(\s+as\s+([\w$]+))?$/,hf=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof B&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},cd="application/json",bc={"Content-Type":cd+";charset=utf-8"},bg=/^\[|^\{(?!\{)/,cg={"[":/]$/,"{":/}$/},ag=/^\)\]\}',?\n/,Dg=O("$http"),gd=function(a){return function(){throw Dg("legacy", +a);}},Ja=ea.$interpolateMinErr=O("$interpolate");Ja.throwNoconcat=function(a){throw Ja("noconcat",a);};Ja.interr=function(a,b){return Ja("interr",a,b.toString())};var Eg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,eg={http:80,https:443,ftp:21},Eb=O("$location"),Fg={$$html5:!1,$$replace:!1,absUrl:Fb("$$absUrl"),url:function(a){if(y(a))return this.$$url;var b=Eg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Fb("$$protocol"), +host:Fb("$$host"),port:Fb("$$port"),path:ld("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(F(a)||Q(a))a=a.toString(),this.$$search=xc(a);else if(G(a))a=qa(a,{}),q(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Eb("isrcharg");break;default:y(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:ld("$$hash",function(a){return null!== +a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};q([kd,ec,dc],function(a){a.prototype=Object.create(Fg);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Eb("nostate");this.$$state=y(b)?null:b;return this}});var ca=O("$parse"),gg=Function.prototype.call,hg=Function.prototype.apply,ig=Function.prototype.bind,Mb=T();q("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Mb[a]=!0});var Gg={n:"\n",f:"\f",r:"\r", +t:"\t",v:"\v","'":"'",'"':'"'},gc=function(a){this.options=a};gc.prototype={constructor:gc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdentifierStart(this.peekMultichar()))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++; +else{var b=a+this.peek(),d=b+this.peek(2),c=Mb[b],e=Mb[d];Mb[a]||c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a|| +"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdentifierStart:function(a){return this.options.isIdentifierStart?this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a, +b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0):(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=x(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw ca("lexerr", +a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<this.text.length;){var d=P(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})}, +readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index<this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)|| +this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,16))):d+=Gg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression"; +s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal="Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.LocalsExpression="LocalsExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a); +a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()}, +assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a= +this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(), +b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")): +this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.selfReferential.hasOwnProperty(this.peek().text)?a=qa(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:s.Literal,value:this.options.literals[this.consume().text]}:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression, +callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!== +this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}}, +object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,b){throw ca("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0=== +this.tokens.length)throw ca("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw ca("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))? +(this.tokens.shift(),a):!1},selfReferential:{"this":{type:s.ThisExpression},$locals:{type:s.LocalsExpression}}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};aa(c,d.$filter);var e="",f;this.stage="assign";if(f=qd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=od(c.body); +d.stage="inputs";q(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext", +"ifDefined","plus","text",e))(this.$filter,Ta,sa,md,fg,Gb,jg,nd,a);this.state=this.stage=void 0;e.literal=rd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;q(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;q(this.state.filters, +function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,h,k=this,l,n;c=c||C;if(!f&&x(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:q(a.body,function(b,c){k.recurse(b.expression, +void 0,void 0,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:n=this.escape(a.value);this.assign(b,n);c(n);break;case s.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){h=a});n=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,n);c(n);break;case s.BinaryExpression:this.recurse(a.left,void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){h=a});n="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g, +0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,n);c(n);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l", +a.name)+"?l:s"),d.computed=!1,d.name=a.name);Ta(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Hb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())|| +this.nextId();b=b||this.nextId();k.recurse(a.object,g,void 0,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),n=k.ensureSafeObject(k.computedMember(g,h)),k.assign(b,n),d&&(d.computed=!0,d.name=h);else{Ta(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)), +k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));n=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Hb(a.property.name))n=k.ensureSafeObject(n);k.assign(b,n);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),l=[],q(a.arguments,function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),n=h+"("+l.join(",")+")",k.assign(b,n),c(b)):(h=k.nextId(),g={},l= +[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);q(a.arguments,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),n=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):n=h+"("+l.join(",")+")";n=k.ensureSafeObject(n);k.assign(b,n)},function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!pd(a.left))throw ca("lval"); +this.recurse(a.left,void 0,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);n=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,n);c(b||n)})},1);break;case s.ArrayExpression:l=[];q(a.elements,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(a)})});n="["+l.join(",")+"]";this.assign(b,n);c(n);break;case s.ObjectExpression:l=[];q(a.properties,function(a){k.recurse(a.value, +k.nextId(),void 0,function(b){l.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});n="{"+l.join(",")+"}";this.assign(b,n);c(n);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.LocalsExpression:this.assign(b,"l");c("l");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a, +"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+ +"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/[$_a-zA-Z][$_a-zA-Z0-9]*/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a), +";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g= +this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(F(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(Q(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw ca("esc");},nextId:function(a, +b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};td.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;aa(c,d.$filter);var e,f;if(e=qd(c))f=this.recurse(e);e=od(c.body);var g;e&&(g=[],q(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];q(c.body,function(a){h.push(d.recurse(a.expression))});e=0===c.body.length?C:1=== +c.body.length?h[0]:function(a,b){var c;q(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=rd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right), +this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Ta(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Hb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Ta(a.property.name,f.expression), +e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],q(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var m=[],r=0;r<g.length;++r)m.push(g[r](a,c,d,f));a=e.apply(void 0,m,f);return b?{context:void 0,name:void 0,value:a}:a}:function(a, +c,d,n){var m=e(a,c,d,n),r;if(null!=m.value){sa(m.context,f.expression);md(m.value,f.expression);r=[];for(var q=0;q<g.length;++q)r.push(sa(g[q](a,c,d,n),f.expression));r=sa(m.value.apply(m.context,r),f.expression)}return b?{value:r}:r};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,n){var m=c(a,d,g,n);a=e(a,d,g,n);sa(m.value,f.expression);Gb(m.context);m.context[m.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],q(a.elements,function(a){g.push(f.recurse(a))}), +function(a,c,d,e){for(var f=[],r=0;r<g.length;++r)f.push(g[r](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],q(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},r=0;r<g.length;++r)f[g[r].key]=g[r].value(a,c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.LocalsExpression:return function(a,c){return b?{value:c}:c};case s.NGValueParameter:return function(a, +c,d){return b?{value:d}:d}}},"unary+":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=nd(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g); +h=(x(h)?h:0)-(x(c)?c:0);return d?{value:h}:h}},"binary*":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c, +e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f, +g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0, +name:void 0,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:void 0;b&&sa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),n,m;null!=l&&(n=b(f,g,h,k),n+="",Ta(n,e),c&&1!==c&&(Gb(l),l&&!l[n]&&(l[n]={})),m=l[n],sa(m,e));return d?{context:l,name:n,value:m}:m}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&(Gb(g), +g&&!g[b]&&(g[b]={}));h=null!=g?g[b]:void 0;(d||Hb(b))&&sa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var hc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(a,d);this.astCompiler=d.csp?new td(this.ast,b):new sd(this.ast,b)};hc.prototype={constructor:hc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var kg=Object.prototype.valueOf,ta=O("$sce"),oa={HTML:"html",CSS:"css", +URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},mg=O("$compile"),Y=v.document.createElement("a"),xd=ra(v.location.href);yd.$inject=["$document"];Jc.$inject=["$provide"];var Fd=22,Ed=".",jc="0";zd.$inject=["$locale"];Bd.$inject=["$locale"];var xg={yyyy:W("FullYear",4,0,!1,!0),yy:W("FullYear",2,0,!0,!0),y:W("FullYear",1,0,!1,!0),MMMM:ib("Month"),MMM:ib("Month",!0),MM:W("Month",2,1),M:W("Month",1,1),LLLL:ib("Month",!1,!0),dd:W("Date",2),d:W("Date",1),HH:W("Hours",2),H:W("Hours",1),hh:W("Hours",2,-12), +h:W("Hours",1,-12),mm:W("Minutes",2),m:W("Minutes",1),ss:W("Seconds",2),s:W("Seconds",1),sss:W("Milliseconds",3),EEEE:ib("Day"),EEE:ib("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:Hd(2),w:Hd(1),G:kc,GG:kc,GGG:kc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},wg=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, +vg=/^\-?\d+$/;Ad.$inject=["$locale"];var qg=da(P),rg=da(sb);Cd.$inject=["$parse"];var ne=da({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===ma.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),tb={};q(Cb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=xa("ng-"+b),e=d;"checked"===a&&(e=function(a, +b,e){e.ngModel!==e[c]&&d(a,b,e)});tb[c]=function(){return{restrict:"A",priority:100,link:e}}}});q(ad,function(a,b){tb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(zg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});q(["src","srcset","href"],function(a){var b=xa("ng-"+a);tb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"=== +ma.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ca&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Jb={$addControl:C,$$renameControl:function(a,b){a.$name=b},$removeControl:C,$setValidity:C,$setDirty:C,$setPristine:C,$setSubmitted:C};Id.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Rd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||C}return{name:"form", +restrict:a?"EAC":"E",require:["form","^^?form"],controller:Id,compile:function(d,f){d.addClass(Ua).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var m=f[0];if(!("action"in e)){var r=function(b){a.$apply(function(){m.$commitViewValue();m.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",r,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",r,!1)},0,!1)})}(f[1]||m.$$parentForm).$addControl(m);var q=g?c(m.$name):C;g&& +(q(a,m),e.$observe(g,function(b){m.$name!==b&&(q(a,void 0),m.$$parentForm.$$renameControl(m,b),q=c(m.$name),q(a,m))}));d.on("$destroy",function(){m.$$parentForm.$removeControl(m);q(a,void 0);R(m,Jb)})}}}}}]},oe=Rd(),Be=Rd(!0),yg=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,Hg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Ig=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i, +Jg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Sd=/^(\d{4,})-(\d{2})-(\d{2})$/,Td=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,nc=/^(\d{4,})-W(\d\d)$/,Ud=/^(\d{4,})-(\d\d)$/,Vd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Kd=T();q(["date","datetime-local","month","time","week"],function(a){Kd[a]=!0});var Wd={text:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c)},date:kb("date",Sd,Lb(Sd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Td,Lb(Td,"yyyy MM dd HH mm ss sss".split(" ")), +"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Vd,Lb(Vd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",nc,function(a,b){if(fa(a))return a;if(F(a)){nc.lastIndex=0;var d=nc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Gd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:kb("month",Ud,Lb(Ud,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Ld(a,b,d,c);jb(a,b,d,c,e,f);c.$$parserName= +"number";c.$parsers.push(function(a){if(c.$isEmpty(a))return null;if(Jg.test(a))return parseFloat(a)});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!Q(a))throw lb("numfmt",a);a=a.toString()}return a});if(x(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||y(g)||a>=g};d.$observe("min",function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));g=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}if(x(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||y(h)||a<=h};d.$observe("max", +function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));h=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}},url:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||Hg.test(d)}},email:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Ig.test(d)}},radio:function(a,b,d,c){y(d.name)&&b.attr("name",++nb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value, +a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Md(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Md(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return pa(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:C,button:C,submit:C,reset:C, +file:C},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Wd[P(g.type)]||Wd.text)(e,f,g,h[0],b,a,d,c)}}}}],Kg=/^(true|false|\d+)$/,Te=function(){return{restrict:"A",priority:100,compile:function(a,b){return Kg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},te=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b); +return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=y(a)?"":a})}}}}],ve=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=y(a)?"":a})}}}}],ue=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g= +b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Se=da({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),we=mc("",!0),ye=mc("Odd",0),xe=mc("Even",1),ze=La({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ae=[function(){return{restrict:"A",scope:!0,controller:"@", +priority:500}}],Ic={},Lg={blur:!0,focus:!0};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=xa("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Lg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var De=["$animate","$compile",function(a, +b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var h,k,l;d.$watch(e.ngIf,function(d){d?k||g(function(d,f){k=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);h={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),k&&(k.$destroy(),k=null),h&&(l=rb(h.clone),a.leave(l).then(function(){l=null}),h=null))})}}}],Ee=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0, +transclude:"element",controller:ea.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,n,m,r){var q=0,s,w,p,y=function(){w&&(w.remove(),w=null);s&&(s.$destroy(),s=null);p&&(d.leave(p).then(function(){w=null}),w=p,p=null)};c.$watch(f,function(f){var n=function(){!x(h)||h&&!c.$eval(h)||b()},u=++q;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&u===q){var b=c.$new();m.template=a;a=r(b,function(a){y();d.enter(a,null,e).then(n)});s=b;p=a;s.$emit("$includeContentLoaded", +f);c.$eval(g)}},function(){c.$$destroyed||u!==q||(y(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(y(),m.template=null)})}}}}],Ve=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){ma.call(d[0]).match(/SVG/)?(d.empty(),a(Lc(e.template,v.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Fe=La({priority:450,compile:function(){return{pre:function(a, +b,d){a.$eval(d.ngInit)}}}}),Re=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?V(e):e;c.$parsers.push(function(a){if(!y(a)){var b=[];a&&q(a.split(g),function(a){a&&b.push(f?V(a):a)});return b}});c.$formatters.push(function(a){if(K(a))return a.join(e)});c.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",Nd="ng-invalid",Ua="ng-pristine",Kb="ng-dirty",Pd="ng-pending",lb=O("ngModel"),Mg=["$scope", +"$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=void 0;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=void 0;this.$name=l(d.name||"",!1)(a); +this.$$parentForm=Jb;var n=e(d.ngModel),m=n.assign,r=n,s=m,v=null,w,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");r=function(a){var c=n(a);E(c)&&(c=b(a));return c};s=function(a,b){E(n(a))?f(a,{$$$p:b}):m(a,b)}}else if(!n.assign)throw lb("nonassign",d.ngModel,wa(c));};this.$render=C;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};this.$$updateEmptyClasses=function(a){p.$isEmpty(a)?(f.removeClass(c,"ng-not-empty"), +f.addClass(c,"ng-empty")):(f.removeClass(c,"ng-empty"),f.addClass(c,"ng-not-empty"))};var H=0;Jd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;f.removeClass(c,Kb);f.addClass(c,Ua)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;f.removeClass(c,Ua);f.addClass(c,Kb);p.$$parentForm.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")}; +this.$setTouched=function(){p.$touched=!0;p.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(v);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!Q(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,b=p.$valid,c=p.$modelValue,d=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(e){d||b===e||(p.$modelValue=e?a:void 0,p.$modelValue!==c&&p.$$writeModelToScope())})}}; +this.$$runValidators=function(a,b,c){function d(){var c=!0;q(p.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(q(p.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;q(p.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!E(h.then))throw lb("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},C):g(!0)}function f(a,b){h===H&&p.$setValidity(a,b)}function g(a){h===H&&c(a)}H++;var h= +H;(function(){var a=p.$$parserName||"parse";if(y(w))f(a,null);else return w||(q(p.$validators,function(a,b){f(b,null)}),q(p.$asyncValidators,function(a,b){f(b,null)})),f(a,w),w;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=p.$viewValue;g.cancel(v);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$updateEmptyClasses(a),p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=p.$$lastCommittedViewValue; +if(w=y(b)?void 0:!0)for(var c=0;c<p.$parsers.length;c++)if(b=p.$parsers[c](b),y(b)){w=!1;break}Q(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=r(a));var d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=b;e&&(p.$modelValue=b,p.$modelValue!==d&&p.$$writeModelToScope());p.$$runValidators(b,p.$$lastCommittedViewValue,function(a){e||(p.$modelValue=a?b:void 0,p.$modelValue!==d&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,p.$modelValue);q(p.$viewChangeListeners, +function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){p.$viewValue=a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=p.$options;d&&x(d.debounce)&&(d=d.debounce,Q(d)?c=d:Q(d[b])?c=d[b]:Q(d["default"])&&(c=d["default"]));g.cancel(v);c?v=g(function(){p.$commitViewValue()},c):h.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var b=r(a);if(b!==p.$modelValue&&(p.$modelValue=== +p.$modelValue||b===b)){p.$modelValue=p.$$rawModelValue=b;w=void 0;for(var c=p.$formatters,d=c.length,e=b;d--;)e=c[d](e);p.$viewValue!==e&&(p.$$updateEmptyClasses(e),p.$viewValue=p.$$lastCommittedViewValue=e,p.$render(),p.$$runValidators(b,e,C))}return b})}],Qe=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:Mg,priority:1,compile:function(b){b.addClass(Ua).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]|| +g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],Ng=/(\s+|^)default(\s+|$)/,Ue=function(){return{restrict:"A", +controller:["$scope","$attrs",function(a,b){var d=this;this.$options=qa(a.$eval(b.ngModelOptions));x(this.$options.updateOn)?(this.$options.updateOnDefault=!1,this.$options.updateOn=V(this.$options.updateOn.replace(Ng,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ge=La({terminal:!0,priority:1E3}),Og=O("ngOptions"),Pg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, +Oe=["$compile","$document","$parse",function(a,b,d){function c(a,b,c){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function f(a){var b;if(!q&&ya(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var m=a.match(Pg);if(!m)throw Og("iexp",a,wa(b));var r=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var s=m[9];b=d(m[2]?m[1]:r);var w=a&&d(a)||b,p=s&&d(s),v=s?function(a,b){return p(c,b)}:function(a){return Fa(a)}, +t=function(a,b){return v(a,L(a,b))},z=d(m[2]||m[1]),u=d(m[3]||""),y=d(m[4]||""),x=d(m[8]),D={},L=q?function(a,b){D[q]=b;D[r]=a;return D}:function(a){D[r]=a;return D};return{trackBy:s,getTrackByValue:t,getWatchables:d(x,function(a){var b=[];a=a||[];for(var d=f(a),e=d.length,g=0;g<e;g++){var h=a===d?g:d[g],l=a[h],h=L(l,h),l=v(l,h);b.push(l);if(m[2]||m[1])l=z(c,h),b.push(l);m[4]&&(h=y(c,h),b.push(h))}return b}),getOptions:function(){for(var a=[],b={},d=x(c)||[],g=f(d),h=g.length,m=0;m<h;m++){var p=d=== +g?m:g[m],q=L(d[p],p),r=w(c,q),p=v(r,q),D=z(c,q),N=u(c,q),q=y(c,q),r=new e(p,r,D,N,q);a.push(r);b[p]=r}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[t(a)]},getViewValueFromOption:function(a){return s?ea.copy(a.viewValue):a.viewValue}}}}}var e=v.document.createElement("option"),f=v.document.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=C},post:function(d,h,k,l){function n(a,b){a.element= +b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function m(){var a=u&&r.readValue();if(u)for(var b=u.items.length-1;0<=b;b--){var c=u.items[b];c.group?Bb(c.element.parentNode):Bb(c.element)}u=I.getOptions();var d={};t&&h.prepend(w);u.items.forEach(function(a){var b;if(x(a.group)){b=d[a.group];b||(b=f.cloneNode(!1),E.appendChild(b),b.label=a.group,d[a.group]=b);var c=e.cloneNode(!1)}else b=E,c=e.cloneNode(!1);b.appendChild(c); +n(a,c)});h[0].appendChild(E);s.$render();s.$isEmpty(a)||(b=r.readValue(),(I.trackBy||v?pa(a,b):a===b)||(s.$setViewValue(b),s.$render()))}var r=l[0],s=l[1],v=k.multiple,w;l=0;for(var p=h.children(),y=p.length;l<y;l++)if(""===p[l].value){w=p.eq(l);break}var t=!!w,z=B(e.cloneNode(!1));z.val("?");var u,I=c(k.ngOptions,h,d),E=b[0].createDocumentFragment();v?(s.$isEmpty=function(a){return!a||0===a.length},r.writeValue=function(a){u.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){if(a= +u.getOptionFromViewValue(a))a.element.selected=!0})},r.readValue=function(){var a=h.val()||[],b=[];q(a,function(a){(a=u.selectValueMap[a])&&!a.disabled&&b.push(u.getViewValueFromOption(a))});return b},I.trackBy&&d.$watchCollection(function(){if(K(s.$viewValue))return s.$viewValue.map(function(a){return I.getTrackByValue(a)})},function(){s.$render()})):(r.writeValue=function(a){var b=u.getOptionFromViewValue(a);b?(h[0].value!==b.selectValue&&(z.remove(),t||w.remove(),h[0].value=b.selectValue,b.element.selected= +!0),b.element.setAttribute("selected","selected")):null===a||t?(z.remove(),t||h.prepend(w),h.val(""),w.prop("selected",!0),w.attr("selected",!0)):(t||w.remove(),h.prepend(z),h.val("?"),z.prop("selected",!0),z.attr("selected",!0))},r.readValue=function(){var a=u.selectValueMap[h.val()];return a&&!a.disabled?(t||w.remove(),z.remove(),u.getViewValueFromOption(a)):null},I.trackBy&&d.$watch(function(){return I.getTrackByValue(s.$viewValue)},function(){s.$render()}));t?(w.remove(),a(w)(d),w.removeClass("ng-scope")): +w=B(e.cloneNode(!1));h.empty();m();d.$watchCollection(I.getWatchables,m)}}}}],He=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,g,h){function k(a){g.text(a||"")}var l=h.count,n=h.$attr.when&&g.attr(h.$attr.when),m=h.offset||0,r=f.$eval(n)||{},s={},v=b.startSymbol(),w=b.endSymbol(),p=v+l+"-"+m+w,x=ea.noop,t;q(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+P(c[2]),r[c]=g.attr(h.$attr[b]))});q(r,function(a,d){s[d]=b(a.replace(c,p))});f.$watch(l, +function(b){var c=parseFloat(b),e=isNaN(c);e||c in r||(c=a.pluralCat(c-m));c===t||e&&Q(t)&&isNaN(t)||(x(),e=s[c],y(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+n),x=C,k()):x=f.$watch(e,k),t=c)})}}}],Ie=["$parse","$animate","$compile",function(a,b,d){var c=O("ngRepeat"),e=function(a,b,c,d,e,n,m){a[c]=d;e&&(a[e]=n);a.$index=b;a.$first=0===b;a.$last=b===m-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3, +terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,k=d.$$createComment("end ngRepeat",h),l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!l)throw c("iexp",h);var n=l[1],m=l[2],r=l[3],s=l[4],l=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!l)throw c("iidexp",n);var v=l[3]||l[1],w=l[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw c("badident", +r);var p,y,t,z,u={$id:Fa};s?p=a(s):(t=function(a,b){return Fa(b)},z=function(a){return a});return function(a,d,f,g,l){p&&(y=function(b,c,d){w&&(u[w]=b);u[v]=c;u.$index=d;return p(a,u)});var n=T();a.$watchCollection(m,function(f){var g,m,p=d[0],s,u=T(),x,D,E,C,F,B,G;r&&(a[r]=f);if(ya(f))F=f,m=y||t;else for(G in m=y||z,F=[],f)ua.call(f,G)&&"$"!==G.charAt(0)&&F.push(G);x=F.length;G=Array(x);for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],C=m(D,E,g),n[C])B=n[C],delete n[C],u[C]=B,G[g]=B;else{if(u[C])throw q(G, +function(a){a&&a.scope&&(n[a.id]=a)}),c("dupes",h,C,E);G[g]={id:C,scope:void 0,clone:void 0};u[C]=!0}for(s in n){B=n[s];C=rb(B.clone);b.leave(C);if(C[0].parentNode)for(g=0,m=C.length;g<m;g++)C[g].$$NG_REMOVED=!0;B.scope.$destroy()}for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],B=G[g],B.scope){s=p;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);B.clone[0]!=s&&b.move(rb(B.clone),null,p);p=B.clone[B.clone.length-1];e(B.scope,g,v,E,w,D,x)}else l(function(a,c){B.scope=c;var d=k.cloneNode(!1);a[a.length++]=d;b.enter(a, +null,p);p=d;B.clone=a;u[B.id]=B;e(B.scope,g,v,E,w,D,x)});n=u})}}}}],Je=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ce=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ke=La(function(a,b,d){a.$watch(d.ngStyle,function(a, +d){d&&a!==d&&q(d,function(a,c){b.css(c,"")});a&&b.css(a)},!0)}),Le=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],h=[],k=[],l=[],n=function(a,b){return function(){a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=k.length;d<e;++d)a.cancel(k[d]);d=k.length=0;for(e=l.length;d<e;++d){var s=rb(h[d].clone);l[d].$destroy();(k[d]=a.leave(s)).then(n(k,d))}h.length=0;l.length=0;(g=f.cases["!"+ +c]||f.cases["?"])&&q(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Me=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),Ne=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a, +b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),Qg=O("ngTransclude"),Pe=La({restrict:"EAC",link:function(a,b,d,c,e){d.ngTransclude===d.$attr.ngTransclude&&(d.ngTransclude="");if(!e)throw Qg("orphan",wa(b));e(function(a){a.length&&(b.empty(),b.append(a))},null,d.ngTransclude||d.ngTranscludeSlot)}}),pe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Rg={$setViewValue:C,$render:C}, +Sg=["$element","$scope",function(a,b){var d=this,c=new Ra;d.ngModelCtrl=Rg;d.unknownOption=B(v.document.createElement("option"));d.renderUnknownOption=function(b){b="? "+Fa(b)+" ?";d.unknownOption.val(b);a.prepend(d.unknownOption);a.val(b)};b.$on("$destroy",function(){d.renderUnknownOption=C});d.removeUnknownOption=function(){d.unknownOption.parent()&&d.unknownOption.remove()};d.readValue=function(){d.removeUnknownOption();return a.val()};d.writeValue=function(b){d.hasOption(b)?(d.removeUnknownOption(), +a.val(b),""===b&&d.emptyOption.prop("selected",!0)):null==b&&d.emptyOption?(d.removeUnknownOption(),a.val("")):d.renderUnknownOption(b)};d.addOption=function(a,b){if(8!==b[0].nodeType){Qa(a,'"option value"');""===a&&(d.emptyOption=b);var g=c.get(a)||0;c.put(a,g+1);d.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=!0)}};d.removeOption=function(a){var b=c.get(a);b&&(1===b?(c.remove(a),""===a&&(d.emptyOption=void 0)):c.put(a,b-1))};d.hasOption=function(a){return!!c.get(a)};d.registerOption= +function(a,b,c,h,k){if(h){var l;c.$observe("value",function(a){x(l)&&d.removeOption(l);l=a;d.addOption(a,b)})}else k?a.$watch(k,function(a,e){c.$set("value",a);e!==a&&d.removeOption(e);d.addOption(a,b)}):d.addOption(c.value,b);b.on("$destroy",function(){d.removeOption(c.value);d.ngModelCtrl.$render()})}}],qe=function(){return{restrict:"E",require:["select","?ngModel"],controller:Sg,priority:1,link:{pre:function(a,b,d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})}); +if(d.multiple){f.readValue=function(){var a=[];q(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Ra(a);q(b.find("option"),function(a){a.selected=x(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==e.$viewValue||pa(g,e.$viewValue)||(g=ha(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||0===a.length}}}},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},se=["$interpolate", +function(a){return{restrict:"E",priority:100,compile:function(b,d){if(x(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],re=da({restrict:"E",terminal:!1}),Fc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required", +function(){c.$validate()}))}}},Ec=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){F(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw O("ngPattern")("noregexp",f,a,wa(b));e=a||void 0;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||y(e)||e.test(b)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a= +X(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=X(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};v.angular.bootstrap?v.console&&console.log("WARNING: Tried to load angular more than once."):(ie(),ke(ea),ea.module("ngLocale",[],["$provide",function(a){function b(a){a+= +"";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "), +WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a, +c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(v.document).ready(function(){ee(v.document,yc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>'); //# sourceMappingURL=angular.min.js.map diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js.map b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js.map deleted file mode 100644 index a57c997f9423fb1d21f208c47ceb497cd4d5471c..0000000000000000000000000000000000000000 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/js/angular/angular.min.js.map +++ /dev/null @@ -1,8 +0,0 @@ -{ -"version":3, -"file":"angular.min.js", -"lineCount":293, -"mappings":"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", -"sources":["angular.js"], -"names":["window","document","undefined","minErr","isArrayLike","obj","isWindow","length","Object","nodeType","NODE_TYPE_ELEMENT","isString","isArray","forEach","iterator","context","key","isFunction","hasOwnProperty","call","isPrimitive","isBlankObject","forEachSorted","keys","sort","i","reverseParams","iteratorFn","value","nextUid","uid","setHashKey","h","$$hashKey","baseExtend","dst","objs","deep","ii","isObject","j","jj","src","isDate","Date","valueOf","isRegExp","RegExp","extend","slice","arguments","merge","toInt","str","parseInt","inherit","parent","extra","create","noop","identity","$","valueFn","hasCustomToString","toString","prototype","isUndefined","isDefined","getPrototypeOf","isNumber","isScope","$evalAsync","$watch","isBoolean","isElement","node","nodeName","prop","attr","find","makeMap","items","split","nodeName_","element","lowercase","arrayRemove","array","index","indexOf","splice","copy","source","destination","stackSource","stackDest","ngMinErr","TYPED_ARRAY_REGEXP","test","push","constructor","getTime","match","lastIndex","cloneNode","emptyObject","shallowCopy","charAt","equals","o1","o2","t1","t2","keySet","createMap","concat","array1","array2","bind","self","fn","curryArgs","startIndex","apply","toJsonReplacer","val","toJson","pretty","JSON","stringify","fromJson","json","parse","timezoneToOffset","timezone","fallback","requestedTimezoneOffset","isNaN","convertTimezoneToLocal","date","reverse","timezoneOffset","getTimezoneOffset","setMinutes","getMinutes","minutes","startingTag","jqLite","clone","empty","e","elemHtml","append","html","NODE_TYPE_TEXT","replace","tryDecodeURIComponent","decodeURIComponent","parseKeyValue","keyValue","splitPoint","substring","toKeyValue","parts","arrayValue","encodeUriQuery","join","encodeUriSegment","pctEncodeSpaces","encodeURIComponent","getNgAttribute","ngAttr","ngAttrPrefixes","getAttribute","angularInit","bootstrap","appElement","module","config","prefix","name","hasAttribute","candidate","querySelector","strictDi","modules","defaultConfig","doBootstrap","injector","tag","unshift","$provide","debugInfoEnabled","$compileProvider","createInjector","invoke","bootstrapApply","scope","compile","$apply","data","NG_ENABLE_DEBUG_INFO","NG_DEFER_BOOTSTRAP","angular","resumeBootstrap","angular.resumeBootstrap","extraModules","resumeDeferredBootstrap","reloadWithDebugInfo","location","reload","getTestability","rootElement","get","snake_case","separator","SNAKE_CASE_REGEXP","letter","pos","toLowerCase","bindJQuery","originalCleanData","bindJQueryFired","jqName","jq","jQuery","on","JQLitePrototype","isolateScope","controller","inheritedData","cleanData","jQuery.cleanData","elems","events","skipDestroyOnNextJQueryCleanData","elem","_data","$destroy","triggerHandler","JQLite","assertArg","arg","reason","assertArgFn","acceptArrayAnnotation","assertNotHasOwnProperty","getter","path","bindFnToScope","lastInstance","len","getBlockNodes","nodes","endNode","blockNodes","nextSibling","setupModuleLoader","ensure","factory","$injectorMinErr","$$minErr","requires","configFn","invokeLater","provider","method","insertMethod","queue","invokeQueue","moduleInstance","invokeLaterAndSetModuleName","recipeName","factoryFunction","$$moduleName","configBlocks","runBlocks","_invokeQueue","_configBlocks","_runBlocks","service","constant","decorator","animation","filter","directive","run","block","publishExternalAPI","version","uppercase","counter","csp","angularModule","ngModule","$$sanitizeUri","$$SanitizeUriProvider","$CompileProvider","a","htmlAnchorDirective","input","inputDirective","textarea","form","formDirective","script","scriptDirective","select","selectDirective","style","styleDirective","option","optionDirective","ngBind","ngBindDirective","ngBindHtml","ngBindHtmlDirective","ngBindTemplate","ngBindTemplateDirective","ngClass","ngClassDirective","ngClassEven","ngClassEvenDirective","ngClassOdd","ngClassOddDirective","ngCloak","ngCloakDirective","ngController","ngControllerDirective","ngForm","ngFormDirective","ngHide","ngHideDirective","ngIf","ngIfDirective","ngInclude","ngIncludeDirective","ngInit","ngInitDirective","ngNonBindable","ngNonBindableDirective","ngPluralize","ngPluralizeDirective","ngRepeat","ngRepeatDirective","ngShow","ngShowDirective","ngStyle","ngStyleDirective","ngSwitch","ngSwitchDirective","ngSwitchWhen","ngSwitchWhenDirective","ngSwitchDefault","ngSwitchDefaultDirective","ngOptions","ngOptionsDirective","ngTransclude","ngTranscludeDirective","ngModel","ngModelDirective","ngList","ngListDirective","ngChange","ngChangeDirective","pattern","patternDirective","ngPattern","required","requiredDirective","ngRequired","minlength","minlengthDirective","ngMinlength","maxlength","maxlengthDirective","ngMaxlength","ngValue","ngValueDirective","ngModelOptions","ngModelOptionsDirective","ngIncludeFillContentDirective","ngAttributeAliasDirectives","ngEventDirectives","$anchorScroll","$AnchorScrollProvider","$animate","$AnimateProvider","$animateCss","$CoreAnimateCssProvider","$$animateQueue","$$CoreAnimateQueueProvider","$$AnimateRunner","$$CoreAnimateRunnerProvider","$browser","$BrowserProvider","$cacheFactory","$CacheFactoryProvider","$controller","$ControllerProvider","$document","$DocumentProvider","$exceptionHandler","$ExceptionHandlerProvider","$filter","$FilterProvider","$$forceReflow","$$ForceReflowProvider","$interpolate","$InterpolateProvider","$interval","$IntervalProvider","$http","$HttpProvider","$httpParamSerializer","$HttpParamSerializerProvider","$httpParamSerializerJQLike","$HttpParamSerializerJQLikeProvider","$httpBackend","$HttpBackendProvider","$xhrFactory","$xhrFactoryProvider","$location","$LocationProvider","$log","$LogProvider","$parse","$ParseProvider","$rootScope","$RootScopeProvider","$q","$QProvider","$$q","$$QProvider","$sce","$SceProvider","$sceDelegate","$SceDelegateProvider","$sniffer","$SnifferProvider","$templateCache","$TemplateCacheProvider","$templateRequest","$TemplateRequestProvider","$$testability","$$TestabilityProvider","$timeout","$TimeoutProvider","$window","$WindowProvider","$$rAF","$$RAFProvider","$$jqLite","$$jqLiteProvider","$$HashMap","$$HashMapProvider","$$cookieReader","$$CookieReaderProvider","camelCase","SPECIAL_CHARS_REGEXP","_","offset","toUpperCase","MOZ_HACK_REGEXP","jqLiteAcceptsData","NODE_TYPE_DOCUMENT","jqLiteBuildFragment","tmp","fragment","createDocumentFragment","HTML_REGEXP","appendChild","createElement","TAG_NAME_REGEXP","exec","wrap","wrapMap","_default","innerHTML","XHTML_TAG_REGEXP","lastChild","childNodes","firstChild","textContent","createTextNode","argIsString","trim","jqLiteMinErr","parsed","SINGLE_TAG_REGEXP","jqLiteAddNodes","jqLiteClone","jqLiteDealoc","onlyDescendants","jqLiteRemoveData","querySelectorAll","descendants","l","jqLiteOff","type","unsupported","expandoStore","jqLiteExpandoStore","handle","listenerFns","removeEventListener","expandoId","ng339","jqCache","createIfNecessary","jqId","jqLiteData","isSimpleSetter","isSimpleGetter","massGetter","jqLiteHasClass","selector","jqLiteRemoveClass","cssClasses","setAttribute","cssClass","jqLiteAddClass","existingClasses","root","elements","jqLiteController","jqLiteInheritedData","documentElement","names","parentNode","NODE_TYPE_DOCUMENT_FRAGMENT","host","jqLiteEmpty","removeChild","jqLiteRemove","keepData","jqLiteDocumentLoaded","action","win","readyState","setTimeout","getBooleanAttrName","booleanAttr","BOOLEAN_ATTR","BOOLEAN_ELEMENTS","createEventHandler","eventHandler","event","isDefaultPrevented","event.isDefaultPrevented","defaultPrevented","eventFns","eventFnsLength","immediatePropagationStopped","originalStopImmediatePropagation","stopImmediatePropagation","event.stopImmediatePropagation","stopPropagation","isImmediatePropagationStopped","event.isImmediatePropagationStopped","$get","this.$get","hasClass","classes","addClass","removeClass","hashKey","nextUidFn","objType","HashMap","isolatedUid","this.nextUid","put","anonFn","args","fnText","STRIP_COMMENTS","FN_ARGS","modulesToLoad","supportObject","delegate","provider_","providerInjector","instantiate","providerCache","providerSuffix","enforceReturnValue","enforcedReturnValue","result","instanceInjector","factoryFn","enforce","loadModules","moduleFn","runInvokeQueue","invokeArgs","loadedModules","message","stack","createInternalInjector","cache","getService","serviceName","caller","INSTANTIATING","err","shift","locals","$inject","$$annotate","Type","instance","returnedValue","annotate","has","$injector","instanceCache","decorFn","origProvider","orig$get","origProvider.$get","origInstance","$delegate","autoScrollingEnabled","disableAutoScrolling","this.disableAutoScrolling","getFirstAnchor","list","Array","some","scrollTo","scrollIntoView","scroll","yOffset","getComputedStyle","position","getBoundingClientRect","bottom","elemTop","top","scrollBy","hash","elm","getElementById","getElementsByName","autoScrollWatch","autoScrollWatchAction","newVal","oldVal","mergeClasses","b","splitClasses","klass","prepareAnimateOptions","options","Browser","completeOutstandingRequest","outstandingRequestCount","outstandingRequestCallbacks","pop","error","cacheStateAndFireUrlChange","pendingLocation","cacheState","fireUrlChange","history","state","cachedState","lastCachedState","lastBrowserUrl","url","lastHistoryState","urlChangeListeners","listener","clearTimeout","pendingDeferIds","isMock","$$completeOutstandingRequest","$$incOutstandingRequestCount","self.$$incOutstandingRequestCount","notifyWhenNoOutstandingRequests","self.notifyWhenNoOutstandingRequests","callback","href","baseElement","self.url","sameState","sameBase","stripHash","substr","self.state","urlChangeInit","onUrlChange","self.onUrlChange","$$applicationDestroyed","self.$$applicationDestroyed","off","$$checkUrlChange","baseHref","self.baseHref","defer","self.defer","delay","timeoutId","cancel","self.defer.cancel","deferId","cacheFactory","cacheId","refresh","entry","freshEnd","staleEnd","n","link","p","nextEntry","prevEntry","caches","size","stats","id","capacity","Number","MAX_VALUE","lruHash","lruEntry","remove","removeAll","destroy","info","cacheFactory.info","cacheFactory.get","$$sanitizeUriProvider","parseIsolateBindings","directiveName","isController","LOCAL_REGEXP","bindings","definition","scopeName","$compileMinErr","mode","collection","optional","attrName","assertValidDirectiveName","hasDirectives","COMMENT_DIRECTIVE_REGEXP","CLASS_DIRECTIVE_REGEXP","ALL_OR_NOTHING_ATTRS","REQUIRE_PREFIX_REGEXP","EVENT_HANDLER_ATTR_REGEXP","this.directive","registerDirective","directiveFactory","Suffix","directives","priority","require","restrict","bindToController","controllerAs","CNTRL_REG","$$bindings","$$isolateBindings","aHrefSanitizationWhitelist","this.aHrefSanitizationWhitelist","regexp","imgSrcSanitizationWhitelist","this.imgSrcSanitizationWhitelist","this.debugInfoEnabled","enabled","safeAddClass","$element","className","$compileNodes","transcludeFn","maxPriority","ignoreDirective","previousCompileContext","nodeValue","compositeLinkFn","compileNodes","$$addScopeClass","namespace","publicLinkFn","cloneConnectFn","parentBoundTranscludeFn","transcludeControllers","futureParentElement","$$boundTransclude","$linkNode","wrapTemplate","controllerName","$$addScopeInfo","nodeList","$rootElement","childLinkFn","childScope","childBoundTranscludeFn","stableNodeList","nodeLinkFnFound","linkFns","idx","nodeLinkFn","destroyBindings","$new","$$destroyBindings","$on","transcludeOnThisElement","createBoundTranscludeFn","transclude","templateOnThisElement","attrs","linkFnFound","Attributes","collectDirectives","applyDirectivesToNode","$$element","terminal","previousBoundTranscludeFn","boundTranscludeFn","transcludedScope","cloneFn","controllers","containingScope","$$transcluded","attrsMap","$attr","addDirective","directiveNormalize","isNgAttr","nAttrs","attributes","attrStartName","attrEndName","ngAttrName","NG_ATTR_BINDING","PREFIX_REGEXP","directiveNName","directiveIsMultiElement","nName","addAttrInterpolateDirective","animVal","msie","addTextInterpolateDirective","NODE_TYPE_COMMENT","byPriority","groupScan","attrStart","attrEnd","depth","groupElementsLinkFnWrapper","linkFn","compileNode","templateAttrs","jqCollection","originalReplaceDirective","preLinkFns","postLinkFns","addLinkFns","pre","post","newIsolateScopeDirective","$$isolateScope","cloneAndAnnotateFn","getControllers","elementControllers","inheritType","dataName","setupControllers","controllerDirectives","controllerKey","$scope","$attrs","$transclude","controllerInstance","hasElementTranscludeDirective","linkNode","thisLinkFn","controllersBoundTransclude","cloneAttachFn","scopeToChild","templateDirective","$$originalDirective","initializeDirectiveBindings","scopeDirective","newScopeDirective","controllerForBindings","identifier","controllerResult","invokeLinkFn","template","templateUrl","terminalPriority","nonTlbTranscludeDirective","hasTranscludeDirective","hasTemplate","$compileNode","$template","childTranscludeFn","$$start","$$end","directiveValue","assertNoDuplicate","$$tlb","createComment","replaceWith","replaceDirective","contents","denormalizeTemplate","removeComments","templateNamespace","newTemplateAttrs","templateDirectives","unprocessedDirectives","markDirectivesAsIsolate","mergeTemplateAttributes","compileTemplateUrl","Math","max","tDirectives","startAttrName","endAttrName","multiElement","srcAttr","dstAttr","$set","tAttrs","linkQueue","afterTemplateNodeLinkFn","afterTemplateChildLinkFn","beforeTemplateCompileNode","origAsyncDirective","derivedSyncDirective","then","content","tempTemplateAttrs","beforeTemplateLinkNode","linkRootElement","$$destroyed","oldClasses","delayedNodeLinkFn","ignoreChildLinkFn","diff","what","previousDirective","wrapModuleNameIfDefined","moduleName","text","interpolateFn","textInterpolateCompileFn","templateNode","templateNodeParent","hasCompileParent","$$addBindingClass","textInterpolateLinkFn","$$addBindingInfo","expressions","interpolateFnWatchAction","wrapper","getTrustedContext","attrNormalizedName","HTML","RESOURCE_URL","allOrNothing","trustedContext","attrInterpolatePreLinkFn","$$observers","newValue","$$inter","$$scope","oldValue","$updateClass","elementsToRemove","newNode","firstElementToRemove","removeCount","j2","replaceChild","hasData","expando","k","kk","annotation","newScope","onNewScopeDestroyed","lastValue","parentGet","parentSet","compare","$observe","literal","assign","parentValueWatch","parentValue","$stateful","unwatch","$watchCollection","attributesToCopy","$normalize","$addClass","classVal","$removeClass","newClasses","toAdd","tokenDifference","toRemove","writeAttr","booleanKey","aliasedKey","ALIASED_ATTR","observer","trimmedSrcset","srcPattern","rawUris","nbrUrisWith2parts","floor","innerIdx","lastTuple","removeAttr","listeners","startSymbol","endSymbol","binding","isolated","noTemplate","str1","str2","values","tokens1","tokens2","token","jqNodes","globals","register","this.register","allowGlobals","this.allowGlobals","addIdentifier","expression","later","ident","$controllerMinErr","controllerPrototype","exception","cause","serializeValue","v","toISOString","ngParamSerializer","params","jQueryLikeParamSerializer","serialize","toSerialize","topLevel","defaultHttpResponseTransform","headers","tempData","JSON_PROTECTION_PREFIX","contentType","jsonStart","JSON_START","JSON_ENDS","parseHeaders","line","headerVal","headerKey","headersGetter","headersObj","transformData","status","fns","defaults","transformResponse","transformRequest","d","common","CONTENT_TYPE_APPLICATION_JSON","patch","xsrfCookieName","xsrfHeaderName","paramSerializer","useApplyAsync","this.useApplyAsync","useLegacyPromise","useLegacyPromiseExtensions","this.useLegacyPromiseExtensions","interceptorFactories","interceptors","requestConfig","response","resp","reject","executeHeaderFns","headerContent","processedHeaders","headerFn","header","mergeHeaders","defHeaders","reqHeaders","defHeaderName","lowercaseDefHeaderName","reqHeaderName","chain","serverRequest","reqData","withCredentials","sendReq","promise","when","reversedInterceptors","interceptor","request","requestError","responseError","thenFn","rejectFn","success","promise.success","promise.error","$httpMinErrLegacyFn","done","headersString","statusText","resolveHttpPromise","resolvePromise","$applyAsync","$$phase","deferred","resolve","resolvePromiseWithResult","removePendingReq","pendingRequests","cachedResp","buildUrl","defaultCache","xsrfValue","urlIsSameOrigin","timeout","responseType","serializedParams","interceptorFactory","createShortMethods","createShortMethodsWithData","createXhr","XMLHttpRequest","createHttpBackend","callbacks","$browserDefer","rawDocument","jsonpReq","callbackId","async","body","called","addEventListener","timeoutRequest","jsonpDone","xhr","abort","completeRequest","open","setRequestHeader","onload","xhr.onload","responseText","urlResolve","protocol","getAllResponseHeaders","onerror","onabort","send","this.startSymbol","this.endSymbol","escape","ch","unescapeText","escapedStartRegexp","escapedEndRegexp","mustHaveExpression","parseStringifyInterceptor","getTrusted","$interpolateMinErr","interr","endIndex","parseFns","textLength","expressionPositions","startSymbolLength","exp","endSymbolLength","throwNoconcat","compute","interpolationFn","$$watchDelegate","$watchGroup","interpolateFnWatcher","oldValues","currValue","$interpolate.startSymbol","$interpolate.endSymbol","interval","count","invokeApply","hasParams","setInterval","clearInterval","iteration","skipApply","$$intervalId","tick","notify","intervals","interval.cancel","encodePath","segments","parseAbsoluteUrl","absoluteUrl","locationObj","parsedUrl","$$protocol","$$host","hostname","$$port","port","DEFAULT_PORTS","parseAppUrl","relativeUrl","prefixed","$$path","pathname","$$search","search","$$hash","beginsWith","begin","whole","trimEmptyHash","LocationHtml5Url","appBase","appBaseNoFile","basePrefix","$$html5","$$parse","this.$$parse","pathUrl","$locationMinErr","$$compose","this.$$compose","$$url","$$absUrl","$$parseLinkUrl","this.$$parseLinkUrl","relHref","appUrl","prevAppUrl","rewrittenUrl","LocationHashbangUrl","hashPrefix","withoutBaseUrl","withoutHashUrl","windowsFilePathExp","base","firstPathSegmentMatch","LocationHashbangInHtml5Url","locationGetter","property","locationGetterSetter","preprocess","html5Mode","requireBase","rewriteLinks","this.hashPrefix","this.html5Mode","setBrowserUrlWithFallback","oldUrl","oldState","$$state","afterLocationChange","$broadcast","absUrl","LocationMode","initialUrl","lastIndexOf","IGNORE_URI_REGEXP","ctrlKey","metaKey","shiftKey","which","button","target","absHref","preventDefault","initializing","newUrl","newState","$digest","$locationWatch","currentReplace","$$replace","urlOrStateChanged","debug","debugEnabled","this.debugEnabled","flag","formatError","Error","sourceURL","consoleLog","console","logFn","log","hasApply","arg1","arg2","warn","ensureSafeMemberName","fullExpression","$parseMinErr","getStringValue","ensureSafeObject","children","ensureSafeFunction","CALL","APPLY","BIND","ensureSafeAssignContext","Function","ifDefined","plusFn","r","findConstantAndWatchExpressions","ast","allConstants","argsToWatch","AST","Program","expr","Literal","toWatch","UnaryExpression","argument","BinaryExpression","left","right","LogicalExpression","ConditionalExpression","alternate","consequent","Identifier","MemberExpression","object","computed","CallExpression","callee","AssignmentExpression","ArrayExpression","ObjectExpression","properties","ThisExpression","getInputs","lastExpression","isAssignable","assignableAST","NGValueParameter","operator","isLiteral","ASTCompiler","astBuilder","ASTInterpreter","isPossiblyDangerousMemberName","getValueOf","objectValueOf","cacheDefault","cacheExpensive","expressionInputDirtyCheck","oldValueOfValue","inputsWatchDelegate","objectEquality","parsedExpression","prettyPrintExpression","inputExpressions","inputs","lastResult","oldInputValueOf","expressionInputWatch","newInputValue","oldInputValueOfValues","oldInputValues","expressionInputsWatch","changed","oneTimeWatchDelegate","oneTimeWatch","oneTimeListener","old","$$postDigest","oneTimeLiteralWatchDelegate","isAllDefined","allDefined","constantWatchDelegate","constantWatch","constantListener","addInterceptor","interceptorFn","watchDelegate","regularInterceptedExpression","oneTimeInterceptedExpression","noUnsafeEval","$parseOptions","expensiveChecks","$parseOptionsExpensive","oneTime","cacheKey","parseOptions","lexer","Lexer","parser","Parser","qFactory","nextTick","exceptionHandler","callOnce","resolveFn","Promise","simpleBind","scheduleProcessQueue","processScheduled","pending","Deferred","$qMinErr","TypeError","onFulfilled","onRejected","progressBack","catch","finally","handleCallback","$$reject","$$resolve","progress","makePromise","resolved","isResolved","callbackOutput","errback","$Q","Q","resolver","all","promises","results","requestAnimationFrame","webkitRequestAnimationFrame","cancelAnimationFrame","webkitCancelAnimationFrame","webkitCancelRequestAnimationFrame","rafSupported","raf","timer","supported","createChildScopeClass","ChildScope","$$watchers","$$nextSibling","$$childHead","$$childTail","$$listeners","$$listenerCount","$$watchersCount","$id","$$ChildScope","TTL","$rootScopeMinErr","lastDirtyWatch","applyAsyncId","digestTtl","this.digestTtl","destroyChildScope","$event","currentScope","Scope","$parent","$$prevSibling","$root","beginPhase","phase","incrementWatchersCount","current","decrementListenerCount","initWatchVal","flushApplyAsync","applyAsyncQueue","scheduleApplyAsync","isolate","child","watchExp","watcher","last","eq","deregisterWatch","watchExpressions","watchGroupAction","changeReactionScheduled","firstRun","newValues","deregisterFns","shouldCall","deregisterWatchGroup","unwatchFn","watchGroupSubAction","$watchCollectionInterceptor","_value","bothNaN","newItem","oldItem","internalArray","oldLength","changeDetected","newLength","internalObject","veryOldValue","trackVeryOldValue","changeDetector","initRun","$watchCollectionAction","watch","watchers","dirty","ttl","watchLog","logIdx","asyncTask","asyncQueue","$eval","msg","next","postDigestQueue","eventName","this.$watchGroup","$applyAsyncExpression","namedListeners","indexOfListener","$emit","targetScope","listenerArgs","$$asyncQueue","$$postDigestQueue","$$applyAsyncQueue","sanitizeUri","uri","isImage","regex","normalizedVal","adjustMatcher","matcher","$sceMinErr","escapeForRegexp","adjustMatchers","matchers","adjustedMatchers","SCE_CONTEXTS","resourceUrlWhitelist","resourceUrlBlacklist","this.resourceUrlWhitelist","this.resourceUrlBlacklist","matchUrl","generateHolderType","Base","holderType","trustedValue","$$unwrapTrustedValue","this.$$unwrapTrustedValue","holderType.prototype.valueOf","holderType.prototype.toString","htmlSanitizer","trustedValueHolderBase","byType","CSS","URL","JS","trustAs","Constructor","maybeTrusted","allowed","this.enabled","sce","isEnabled","sce.isEnabled","sce.getTrusted","parseAs","sce.parseAs","enumValue","lName","eventSupport","android","userAgent","navigator","boxee","vendorPrefix","vendorRegex","bodyStyle","transitions","animations","webkitTransition","webkitAnimation","pushState","hasEvent","divElm","handleRequestFn","tpl","ignoreRequestError","totalPendingRequests","getTrustedResourceUrl","transformer","httpOptions","handleError","testability","testability.findBindings","opt_exactMatch","getElementsByClassName","matches","dataBinding","bindingName","testability.findModels","prefixes","attributeEquals","testability.getLocation","testability.setLocation","testability.whenStable","deferreds","$$timeoutId","timeout.cancel","urlParsingNode","requestUrl","originUrl","$$CookieReader","safeDecodeURIComponent","lastCookies","lastCookieString","cookieArray","cookie","currentCookieString","filters","suffix","currencyFilter","dateFilter","filterFilter","jsonFilter","limitToFilter","lowercaseFilter","numberFilter","orderByFilter","uppercaseFilter","comparator","matchAgainstAnyProp","getTypeForFilter","expressionType","predicateFn","createPredicateFn","shouldMatchPrimitives","actual","expected","item","deepCompare","dontMatchWholeObject","actualType","expectedType","expectedVal","matchAnyProperty","actualVal","$locale","formats","NUMBER_FORMATS","amount","currencySymbol","fractionSize","CURRENCY_SYM","PATTERNS","maxFrac","formatNumber","GROUP_SEP","DECIMAL_SEP","number","groupSep","decimalSep","isNegative","abs","isInfinity","Infinity","isFinite","numStr","formatedText","hasExponent","toFixed","parseFloat","fractionLen","min","minFrac","round","fraction","lgroup","lgSize","group","gSize","negPre","posPre","negSuf","posSuf","padNumber","num","digits","neg","dateGetter","dateStrGetter","shortForm","getFirstThursdayOfYear","year","dayOfWeekOnFirst","getDay","weekGetter","firstThurs","getFullYear","thisThurs","getMonth","getDate","eraGetter","ERAS","jsonStringToDate","string","R_ISO8601_STR","tzHour","tzMin","dateSetter","setUTCFullYear","setFullYear","timeSetter","setUTCHours","setHours","m","s","ms","format","DATETIME_FORMATS","NUMBER_STRING","DATE_FORMATS_SPLIT","dateTimezoneOffset","DATE_FORMATS","spacing","limit","processPredicates","sortPredicate","reverseOrder","map","predicate","descending","predicates","compareValues","getComparisonObject","predicateValues","doComparison","v1","v2","ngDirective","FormController","controls","$error","$$success","$pending","$name","$dirty","$pristine","$valid","$invalid","$submitted","$$parentForm","nullFormCtrl","$rollbackViewValue","form.$rollbackViewValue","control","$commitViewValue","form.$commitViewValue","$addControl","form.$addControl","$$renameControl","form.$$renameControl","newName","oldName","$removeControl","form.$removeControl","$setValidity","addSetValidityMethod","ctrl","set","unset","$setDirty","form.$setDirty","PRISTINE_CLASS","DIRTY_CLASS","$setPristine","form.$setPristine","setClass","SUBMITTED_CLASS","$setUntouched","form.$setUntouched","$setSubmitted","form.$setSubmitted","stringBasedInputType","$formatters","$isEmpty","baseInputType","composing","ev","ngTrim","$viewValue","$$hasNativeValidators","$setViewValue","deferListener","origValue","keyCode","$render","ctrl.$render","createDateParser","mapping","iso","ISO_DATE_REGEXP","yyyy","MM","dd","HH","getHours","mm","ss","getSeconds","sss","getMilliseconds","part","NaN","createDateInputType","parseDate","dynamicDateInputType","isValidDate","parseObservedDateValue","badInputChecker","$options","previousDate","$$parserName","$parsers","parsedDate","ngModelMinErr","ngMin","minVal","$validators","ctrl.$validators.min","$validate","ngMax","maxVal","ctrl.$validators.max","validity","VALIDITY_STATE_PROPERTY","badInput","typeMismatch","parseConstantExpr","parseFn","classDirective","arrayDifference","arrayClasses","digestClassCounts","classCounts","classesToUpdate","ngClassWatchAction","$index","old$index","mod","cachedToggleClass","switchValue","classCache","toggleValidationCss","validationErrorKey","isValid","VALID_CLASS","INVALID_CLASS","setValidity","isObjectEmpty","PENDING_CLASS","combinedState","REGEX_STRING_REGEXP","documentMode","rules","ngCspElement","ngCspAttribute","noInlineStyle","name_","el","full","major","minor","dot","codeName","JQLite._data","MOUSE_EVENT_MAP","mouseleave","mouseenter","optgroup","tbody","tfoot","colgroup","caption","thead","th","td","ready","trigger","fired","removeData","jqLiteHasData","removeAttribute","css","NODE_TYPE_ATTRIBUTE","lowercasedName","specified","getNamedItem","ret","getText","$dv","multiple","selected","nodeCount","jqLiteOn","types","related","relatedTarget","contains","one","onFn","replaceNode","insertBefore","contentDocument","prepend","wrapNode","detach","after","newElement","toggleClass","condition","classCondition","nextElementSibling","getElementsByTagName","extraParameters","dummyEvent","handlerArgs","eventFnsCopy","arg3","unbind","FN_ARG_SPLIT","FN_ARG","argDecl","underscore","$animateMinErr","AnimateRunner","end","resume","pause","complete","pass","fail","postDigestElements","updateData","handleCSSClassChanges","existing","pin","domOperation","from","to","classesAdded","add","classesRemoved","$$registeredAnimations","classNameFilter","this.classNameFilter","$$classNameFilter","reservedRegex","NG_ANIMATE_CLASSNAME","domInsert","parentElement","afterElement","afterNode","ELEMENT_NODE","previousElementSibling","runner","enter","move","leave","addclass","animate","tempClasses","RAFPromise","getPromise","f1","f2","closed","cleanupStyles","start","domNode","offsetWidth","APPLICATION_JSON","$httpMinErr","$interpolateMinErr.throwNoconcat","$interpolateMinErr.interr","PATH_MATCH","locationPrototype","paramValue","Location","Location.prototype.state","OPERATORS","ESCAPE","lex","tokens","readString","peek","readNumber","isIdent","readIdent","is","isWhitespace","ch2","ch3","op2","op3","op1","throwError","chars","isExpOperator","colStr","peekCh","quote","rawString","hex","String","fromCharCode","rep","ExpressionStatement","Property","program","expressionStatement","expect","filterChain","assignment","ternary","logicalOR","consume","logicalAND","equality","relational","additive","multiplicative","unary","primary","arrayDeclaration","constants","parseArguments","baseExpression","peekToken","kind","e1","e2","e3","e4","peekAhead","t","nextId","vars","own","assignable","stage","computing","recurse","return_","generateFunction","fnKey","intoId","watchId","fnString","USE","STRICT","filterPrefix","watchFns","varsPrefix","section","nameId","recursionFn","skipWatchIdCheck","if_","lazyAssign","computedMember","lazyRecurse","plus","not","getHasOwnProperty","nonComputedMember","addEnsureSafeObject","notNull","addEnsureSafeMemberName","addEnsureSafeFunction","member","addEnsureSafeAssignContext","filterName","defaultValue","stringEscapeRegex","stringEscapeFn","c","charCodeAt","skip","init","fn.assign","rhs","lhs","unary+","unary-","unary!","binary+","binary-","binary*","binary/","binary%","binary===","binary!==","binary==","binary!=","binary<","binary>","binary<=","binary>=","binary&&","binary||","ternary?:","astCompiler","yy","y","MMMM","MMM","M","H","hh","EEEE","EEE","ampmGetter","AMPMS","Z","timeZoneGetter","zone","paddedZone","ww","w","G","GG","GGG","GGGG","longEraGetter","ERANAMES","xlinkHref","propName","defaultLinkFn","normalized","ngBooleanAttrWatchAction","htmlAttr","ngAttrAliasWatchAction","nullFormRenameControl","formDirectiveFactory","isNgForm","getSetter","ngFormCompile","formElement","nameAttr","ngFormPreLink","ctrls","handleFormSubmission","setter","URL_REGEXP","EMAIL_REGEXP","NUMBER_REGEXP","DATE_REGEXP","DATETIMELOCAL_REGEXP","WEEK_REGEXP","MONTH_REGEXP","TIME_REGEXP","inputType","textInputType","weekParser","isoWeek","existingDate","week","hours","seconds","milliseconds","addDays","numberInputType","urlInputType","ctrl.$validators.url","modelValue","viewValue","emailInputType","email","ctrl.$validators.email","radioInputType","checked","checkboxInputType","trueValue","ngTrueValue","falseValue","ngFalseValue","ctrl.$isEmpty","CONSTANT_VALUE_REGEXP","tplAttr","ngValueConstantLink","ngValueLink","valueWatchAction","$compile","ngBindCompile","templateElement","ngBindLink","ngBindWatchAction","ngBindTemplateCompile","ngBindTemplateLink","ngBindHtmlCompile","tElement","ngBindHtmlGetter","ngBindHtmlWatch","ngBindHtmlLink","ngBindHtmlWatchAction","getTrustedHtml","$viewChangeListeners","forceAsyncEvents","ngEventHandler","previousElements","ngIfWatchAction","srcExp","onloadExp","autoScrollExp","autoscroll","changeCounter","previousElement","currentElement","cleanupLastIncludeContent","ngIncludeWatchAction","afterAnimation","thisChangeId","namespaceAdaptedClone","trimValues","NgModelController","$modelValue","$$rawModelValue","$asyncValidators","$untouched","$touched","parsedNgModel","parsedNgModelAssign","ngModelGet","ngModelSet","pendingDebounce","parserValid","$$setOptions","this.$$setOptions","getterSetter","invokeModelGetter","invokeModelSetter","$$$p","this.$isEmpty","currentValidationRunId","this.$setPristine","this.$setDirty","this.$setUntouched","UNTOUCHED_CLASS","TOUCHED_CLASS","$setTouched","this.$setTouched","this.$rollbackViewValue","$$lastCommittedViewValue","this.$validate","prevValid","prevModelValue","allowInvalid","$$runValidators","allValid","$$writeModelToScope","this.$$runValidators","doneCallback","processSyncValidators","syncValidatorsValid","validator","processAsyncValidators","validatorPromises","validationDone","localValidationRunId","processParseErrors","errorKey","this.$commitViewValue","$$parseAndValidate","this.$$parseAndValidate","this.$$writeModelToScope","this.$setViewValue","updateOnDefault","$$debounceViewValueCommit","this.$$debounceViewValueCommit","debounceDelay","debounce","ngModelWatch","formatters","ngModelCompile","ngModelPreLink","modelCtrl","formCtrl","ngModelPostLink","updateOn","DEFAULT_REGEXP","that","ngOptionsMinErr","NG_OPTIONS_REGEXP","parseOptionsExpression","optionsExp","selectElement","Option","selectValue","label","disabled","getOptionValuesKeys","optionValues","optionValuesKeys","keyName","itemKey","valueName","selectAs","trackBy","viewValueFn","trackByFn","getTrackByValueFn","getHashOfValue","getTrackByValue","getLocals","displayFn","groupByFn","disableWhenFn","valuesFn","getWatchables","watchedArray","optionValuesLength","disableWhen","getOptions","optionItems","selectValueMap","optionItem","getOptionFromViewValue","getViewValueFromOption","optionTemplate","optGroupTemplate","updateOptionElement","addOrReuseElement","removeExcessElements","skipEmptyAndUnknownOptions","emptyOption_","emptyOption","unknownOption_","unknownOption","updateOptions","previousValue","selectCtrl","readValue","groupMap","providedEmptyOption","updateOption","optionElement","groupElement","currentOptionElement","ngModelCtrl","nextValue","ngModelCtrl.$isEmpty","writeValue","selectCtrl.writeValue","selectCtrl.readValue","selectedValues","selections","selectedOption","BRACE","IS_WHEN","updateElementText","newText","numberExp","whenExp","whens","whensExpFns","braceReplacement","watchRemover","lastCount","attributeName","tmpMatch","whenKey","ngPluralizeWatchAction","countIsNaN","pluralCat","whenExpFn","ngRepeatMinErr","updateScope","valueIdentifier","keyIdentifier","arrayLength","$first","$last","$middle","$odd","$even","ngRepeatCompile","ngRepeatEndComment","aliasAs","trackByExp","trackByExpGetter","trackByIdExpFn","trackByIdArrayFn","trackByIdObjFn","hashFnLocals","ngRepeatLink","lastBlockMap","ngRepeatAction","previousNode","nextNode","nextBlockMap","collectionLength","trackById","collectionKeys","nextBlockOrder","trackByIdFn","blockKey","ngRepeatTransclude","ngShowWatchAction","NG_HIDE_CLASS","NG_HIDE_IN_PROGRESS_CLASS","ngHideWatchAction","ngStyleWatchAction","newStyles","oldStyles","ngSwitchController","cases","selectedTranscludes","selectedElements","previousLeaveAnimations","selectedScopes","spliceFactory","ngSwitchWatchAction","selectedTransclude","caseElement","selectedScope","anchor","noopNgModelController","SelectController","optionsMap","renderUnknownOption","self.renderUnknownOption","unknownVal","removeUnknownOption","self.removeUnknownOption","self.readValue","self.writeValue","hasOption","addOption","self.addOption","removeOption","self.removeOption","self.hasOption","ngModelCtrl.$render","lastView","lastViewRef","selectMultipleWatch","valueInterpolated","optionValue","selectCtrlName","valueAttributeObserveAction","interpolateWatchAction","ctrl.$validators.required","patternExp","ctrl.$validators.pattern","intVal","ctrl.$validators.maxlength","ctrl.$validators.minlength","getDecimals","opt_precision","pow","ONE","OTHER","$$csp","head"] -} diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/storage.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/storage.py new file mode 100644 index 0000000000000000000000000000000000000000..77a3a31ac73809b91ca51ac1fc087dd3b8efe7ca --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/storage.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# mom.py +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id: mom.py 1580 2015-09-30 14:18:57Z loose $ + +""" +TODO: documentation +""" + +import logging + +logger = logging.getLogger(__name__) + +def updateTaskStorageDetails(tasks, sqrpc): + if not tasks: + return + + def applyDefaults(task): + '''apply sane default values for a task''' + task['disk_usage'] = None + task['disk_usage_readable'] = None + + tasks = tasks if isinstance(tasks, list) else [tasks] + + for task in tasks: + applyDefaults(task) + + statuses = set(['finished', 'completing', 'aborted']) + tasks = [t for t in tasks if t['cluster'] == 'CEP4' and t['status'] in statuses] + + if len(tasks) == 0: + return + + if not sqrpc: + return + + try: + otdb_ids = [t['otdb_id'] for t in tasks] + usages = sqrpc.getDiskUsageForTasks(otdb_ids=otdb_ids, include_scratch_paths=False).get('otdb_ids') + + if not usages: + return + + for task in tasks: + otdb_id = str(task['otdb_id']) + if otdb_id in usages: + usage = usages[otdb_id] + task['disk_usage'] = usage.get('disk_usage') + task['disk_usage_readable'] = usage.get('disk_usage_readable') + + except Exception as e: + logger.error(str(e)) + diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html index 0818ef43f8f240e2d2771e329100588ec14a4cc2..9e5bec8c592849e8c163e3c075977892b3af9e27 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html @@ -10,6 +10,7 @@ <link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css"> <link href="/static/js/angular-ui-grid/ui-grid.min.css" rel="stylesheet" type="text/css"> <link href="/static/js/angular-ui-layout/angular-ui-layout.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-material/angular-material.min.css" rel="stylesheet" type="text/css"> <link href="/static/js/utils/datetimepicker.css" rel="stylesheet" type="text/css"> <link href="/static/css/main.css" rel="stylesheet" type="text/css"> <script src="/static/js/utils/startswith.js"></script> @@ -18,7 +19,7 @@ <script src="/static/js/utils/bootstrap.min.js"></script> <script type="text/javascript" src="/static/js/highcharts/highcharts.js"></script> <script type="text/javascript" src="/static/js/highcharts/exporting.js"></script> - <script src="/static/js/angular/angular.js"></script> + <script src="/static/js/angular/angular.min.js"></script> <script src="/static/js/utils/ui-bootstrap-tpls.min.js"></script> <script src="/static/js/angular-route/angular-route.min.js"></script> <script src="/static/js/angular-touch/angular-touch.js"></script> @@ -27,9 +28,12 @@ <script src="/static/js/angular-ui-grid/ui-grid.js"></script> <script src="/static/js/angular-ui-tree/angular-ui-tree.js"></script> <script src="/static/js/angular-ui-layout/angular-ui-layout.min.js"></script> - <script src="/static/js/angular-ui-layout/angular-ui-layout.min.js"></script> <script src="/static/js/angular-ui-tabs/angular-ui.bootstrap.tabs.min.js"></script> <script src="/static/js/angular-moment/angular-moment.js"></script> + <script src="/static/js/angular-sanitize/angular-sanitize.min.js"></script> + <script src="/static/js/angular-animate/angular-animate.min.js"></script> + <script src="/static/js/angular-aria/angular-aria.min.js"></script> + <script src="/static/js/angular-material/angular-material.min.js"></script> <script src="/static/js/utils/datetimepicker.js"></script> <script src="/static/js/jsplumb/jsplumb-2.0.7-min.js"></script> <script src="/static/js/angular-gantt/angular-gantt.js"></script> @@ -37,111 +41,130 @@ <script type="text/javascript" src="/static/js/highcharts/highcharts-ng.js"></script> <script src="/static/app/app.js"></script> <script src="/static/app/controllers/datacontroller.js"></script> + <script src="/static/app/controllers/cleanupcontroller.js"></script> <script src="/static/app/controllers/gridcontroller.js"></script> <script src="/static/app/controllers/ganttresourcecontroller.js"></script> <script src="/static/app/controllers/ganttprojectcontroller.js"></script> + <script src="/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js"></script> <script src="/static/app/controllers/chartresourceusagecontroller.js"></script> </head> <body style="overflow:hidden;"> {% raw %} <div ng-controller="DataController as dataCtrl" class="container-fluid"> - <div class="row"> - <div class="col-md-2"> + <div ng-controller="CleanupController as cleanupCtrl" class="container-fluid"> + <div> + <div style="float:left; width:220px;"> <label>Time (UTC):</label> <p> <strong style="font-size:16px">{{dataService.lofarTime | date }}</strong> </p> </div> - <div class="col-md-3"> + <div style="float:left; width:300px; padding-right:16px; "> <label>From:</label> <p class="input-group"> - <input type="text" class="form-control" style="min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="dataService.viewTimeSpan.from" is-open="viewFromDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <input type="text" class="form-control" style="min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" is-open="viewFromDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="openViewFromDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> </span> - <uib-timepicker ng-model="dataService.viewTimeSpan.from" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> </p> </div> - <div class="col-md-3"> + <div style="float:left; width:300px; padding-right:16px; "> <label>To:</label> <p class="input-group"> - <input type="text" class="form-control" style="min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="dataService.viewTimeSpan.to" is-open="viewToDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <input type="text" class="form-control" style="min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" is-open="viewToDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="openViewToDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> </span> - <uib-timepicker ng-model="dataService.viewTimeSpan.to" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> </p> </div> - <div class="col-md-3"> - <label>Jump:</label> + <div style="float:left; min-width:90px; padding-right:16px; "> + <label>Scroll:</label> <p class="input-group"> - <span class="input-group-btn"> - <button type="button" class="btn btn-default" ng-click="jumpToSelectedTasks()" title="Jump to selected Task(s)">Task</button> - </span> - <span class="input-group-btn" style="width:10px;"></span> - <span class="input-group-btn"> - <button type="button" class="btn btn-default" ng-click="jumpToNow()" title="Jump to Now">Now</button> - </span> - <select class="form-control" ng-model=jumpTimespanWidth ng-options="option.name for option in jumpTimespanWidths track by option.value" ng-change="onJumpTimespanWidthChanged(span)"> - </select> + <label title="Automatically scroll 'From' and 'To' to watch live events" style="padding-right: 4px; vertical-align: top;">Live <input type="checkbox" ng-model="$parent.dataService.autoFollowNow"></label> + <button title="Scroll back in time" type="button" class="btn btn-default" ng-click="scrollBack()"><i class="glyphicon glyphicon-step-backward"></i></button> + <button title="Scroll forward in time" type="button" class="btn btn-default" ng-click="scrollForward()"><i class="glyphicon glyphicon-step-forward"></i></button> + </p> + </div> + <div style="float:left; min-width:190px; padding-right:16px; "> + <label>Zoom:</label> + <p class="input-group"> + <select class="form-control" ng-model="$parent.zoomTimespan" ng-options="option.name for option in $parent.zoomTimespans track by option.value" ng-change="$parent.onZoomTimespanChanged()"></select> </p> </div> + <div style="float:left; min-width:50px; padding-right:16px; "> + <label>Disk usage:</label> + <p class="input-group"> + <button title="Show disk usage by project" type="button" class="btn btn-default" ng-click="cleanupCtrl.showAllProjectsDiskUsage()"><i class="glyphicon glyphicon-floppy-disk"></i></button> + </p> + </div> + <div style="float:left; min-width:50px; padding-top:30px; font-size:14px;"> + <a href="/projects" target="_blank">Projects</a> + </div> </div> - <div class="top-stretch" ui-layout options="{flow: 'column'}"> - <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" > + <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" ui-layout-init-min-width="1160px"> <div id="grid" ui-grid="gridOptions" ui-grid-edit ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-auto-resize class="grid"></div> </div> - <div ui-layout options="{flow: 'row'}"> - <div ng-controller="GanttProjectController as ganttProjectCtrl" style="overflow:auto; margin-left:12px; margin-bottom:4px; "> - <div gantt data=ganttData - api=options.api - show-side='true' - view-scale="options.viewScale" - from-date="options.fromDate" - to-date="options.toDate" - current-date="options.currentDate" - current-date-value="options.currentDateValue" - column-magnet="options.columnMagnet"> - <gantt-tree enabled="true"></gantt-tree> - <gantt-movable enabled="true" - allow-moving="true" - allow-resizing="true" - allow-row-switching="false"> - </gantt-movable> - <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> - <gantt-dependencies enabled="true"></gantt-dependencies> + <md-content margin-top='10px' style="margin-right: 4px;"> + <md-tabs md-dynamic-height md-border-bottom style="height: 100%; width: 100%;" > + <div ng-controller="GanttProjectController as ganttProjectCtrl" ng-init="enabled=true"> + <md-tab label="Tasks" md-on-select="enabled=true" md-on-deselect="enabled=false"> + <div gantt data=ganttData + api=options.api + show-side='true' + view-scale="options.viewScale" + from-date="options.fromDate" + to-date="options.toDate" + current-date="options.currentDate" + current-date-value="options.currentDateValue" + column-magnet="options.columnMagnet" + style="height: 75%; max-height:1200px; width: 100%; overflow-x: hidden; overflow-y: auto; margin-left: 8px;"> + <gantt-table enabled="true"></gantt-table> + <gantt-movable enabled="true" + allow-moving="true" + allow-resizing="true" + allow-row-switching="false"> + </gantt-movable> + <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> + <gantt-dependencies enabled="options.dependencies"></gantt-dependencies> + <gantt-contextmenu enabled="true"></gantt-contextmenu> + </div> + </md-tab> </div> - </div> - - <div ng-controller="ChartResourceUsageController as chartResourceUsageCtrl"> - <highchart id="chart_resource_usage" config="chartConfig" style="width: 96%; height: 100%; margin: 12px;" ></highchart> - </div> - <div ng-controller="GanttResourceController as ganttResourceCtrl" style="overflow:auto; margin-left:12px; margin-top:12px"> - <div gantt data=ganttData - api=options.api - show-side='true' - view-scale="options.viewScale" - from-date="options.fromDate" - to-date="options.toDate" - current-date="options.currentDate" - current-date-value="options.currentDateValue" - column-magnet="options.columnMagnet"> - <gantt-tree enabled="true"></gantt-tree> - <gantt-movable enabled="true" - allow-moving="true" - allow-resizing="true" - allow-row-switching="false"></gantt-movable> - <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> + <div ng-controller="GanttResourceController as ganttResourceCtrl" ng-init="enabled=false"> + <md-tab label="Resources" md-on-select="enabled=true" md-on-deselect="enabled=false"> + <div gantt data=ganttData + api=options.api + show-side='true' + view-scale="options.viewScale" + from-date="options.fromDate" + to-date="options.toDate" + current-date="options.currentDate" + current-date-value="options.currentDateValue" + column-magnet="options.columnMagnet" + style="height: 75%; max-height:800px; width: 100%; overflow-x: hidden; overflow-y: auto; margin-left: 8px;"> + <gantt-tree enabled="true"></gantt-tree> + <gantt-movable enabled="true" + allow-moving="true" + allow-resizing="true" + allow-row-switching="false"></gantt-movable> + <gantt-tooltips enabled="true" date-format="'YYYY-MM-DD HH:mm'"></gantt-tooltips> + <gantt-contextmenu enabled="true"></gantt-contextmenu> + </div> + <div ng-controller="ChartResourceUsageController as chartResourceUsageCtrl" style="height: 25%"> + <highchart id="chart_resource_usage" config="chartConfig" style="margin: 12px;" ></highchart> + </div> + </md-tab> </div> - </div> - - </div> + </md-tabs> + </md-content> </div> </div> {% endraw %} diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html new file mode 100644 index 0000000000000000000000000000000000000000..264cf31e92b904762bc22dc651b81af10b0d6434 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/projects.html @@ -0,0 +1,121 @@ +<!doctype html> +<!-- $Id: index.html 35449 2016-09-28 06:54:12Z schaap $ --> +<html lang='en' ng-app="raeApp"> + <head> + <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> + <title>{{title}}</title> + <link rel='shortcut icon' href='{{ url_for('static', filename='favicon.ico') }}'> + <link href="/static/js/angular-gantt/angular-gantt.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-gantt/angular-gantt-plugins.min.css" rel="stylesheet" type="text/css"> + <link href="/static/css/bootstrap.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-ui-grid/ui-grid.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-ui-layout/angular-ui-layout.css" rel="stylesheet" type="text/css"> + <link href="/static/js/angular-material/angular-material.min.css" rel="stylesheet" type="text/css"> + <link href="/static/js/utils/datetimepicker.css" rel="stylesheet" type="text/css"> + <link href="/static/css/main.css" rel="stylesheet" type="text/css"> + <script src="/static/js/utils/startswith.js"></script> + <script src="/static/js/moment/moment.js"></script> + <script src="/static/js/jquery/jquery.min.js"></script> + <script src="/static/js/utils/bootstrap.min.js"></script> + <script type="text/javascript" src="/static/js/highcharts/highcharts.js"></script> + <script type="text/javascript" src="/static/js/highcharts/exporting.js"></script> + <script src="/static/js/angular/angular.min.js"></script> + <script src="/static/js/utils/ui-bootstrap-tpls.min.js"></script> + <script src="/static/js/angular-route/angular-route.min.js"></script> + <script src="/static/js/angular-touch/angular-touch.js"></script> + <script src="/static/js/angular-resource/angular-resource.min.js"></script> +<!-- <script src="/static/js/utils/ui-grid-edit-datepicker.js"></script>--> + <script src="/static/js/angular-ui-grid/ui-grid.js"></script> + <script src="/static/js/angular-ui-tree/angular-ui-tree.js"></script> + <script src="/static/js/angular-ui-layout/angular-ui-layout.min.js"></script> + <script src="/static/js/angular-ui-tabs/angular-ui.bootstrap.tabs.min.js"></script> + <script src="/static/js/angular-moment/angular-moment.js"></script> + <script src="/static/js/angular-sanitize/angular-sanitize.min.js"></script> + <script src="/static/js/angular-animate/angular-animate.min.js"></script> + <script src="/static/js/angular-aria/angular-aria.min.js"></script> + <script src="/static/js/angular-material/angular-material.min.js"></script> + <script src="/static/js/utils/datetimepicker.js"></script> + <script src="/static/js/jsplumb/jsplumb-2.0.7-min.js"></script> + <script src="/static/js/angular-gantt/angular-gantt.js"></script> + <script src="/static/js/angular-gantt/angular-gantt-plugins.js"></script> + <script type="text/javascript" src="/static/js/highcharts/highcharts-ng.js"></script> + <script src="/static/app/app.js"></script> + <script src="/static/app/controllers/datacontroller.js"></script> + <script src="/static/app/controllers/cleanupcontroller.js"></script> + <script src="/static/app/controllers/gridcontroller.js"></script> + <script src="/static/app/controllers/ganttresourcecontroller.js"></script> + <script src="/static/app/controllers/ganttprojectcontroller.js"></script> + <script src="/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js"></script> + <script src="/static/app/controllers/chartresourceusagecontroller.js"></script> + </head> + <body style="overflow:hidden;"> + {% raw %} + <div ng-controller="DataController as dataCtrl" class="container-fluid" ng-init="dataService.projectMode=true"> + <div ng-controller="CleanupController as cleanupCtrl" class="container-fluid"> + <div> + <div style="float:left; min-width:250px; padding-right:16px;"> + <label>Project:</label> + <p style="font-size:14px;" > + <select class="form-control ng-pristine" ng-model="dataService.selected_project" ng-options="{ name: item.name, value: item.mom_id } as item.name for item in dataService.momProjects"></select> + </p> + </div> + <div style="float:left; width:220px;"> + <label>Time (UTC):</label> + <p> + <strong style="font-size:16px">{{dataService.lofarTime | date }}</strong> + </p> + </div> + <div style="float:left; width:300px; padding-right:16px; "> + <label>From:</label> + <p class="input-group"> + <input type="text" class="form-control" style="float:left; min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" is-open="viewFromDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="openViewFromDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> + </span> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.from" ng-change="$parent.onViewTimeSpanFromChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + </p> + </div> + <div style="float:left; width:300px; padding-right:16px; "> + <label>To:</label> + <p class="input-group"> + <input type="text" class="form-control" style="float:left; min-width:100px" uib-datepicker-popup="yyyy-MM-dd" ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" is-open="viewToDatePopupOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" close-on-date-selection="false"/> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="openViewToDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button> + </span> + <uib-timepicker ng-model="$parent.dataService.viewTimeSpan.to" ng-change="$parent.onViewTimeSpanToChanged()" hour-step="1" minute-step="5" show-meridian="false" show-spinners="false"></uib-timepicker> + </p> + </div> + <div style="float:left; min-width:90px; padding-right:16px; "> + <label>Scroll:</label> + <p class="input-group"> + <button title="Scroll back in time" type="button" class="btn btn-default" ng-click="scrollBack()"><i class="glyphicon glyphicon-step-backward"></i></button> + <button title="Scroll forward in time" type="button" class="btn btn-default" ng-click="scrollForward()"><i class="glyphicon glyphicon-step-forward"></i></button> + </p> + </div> + <div style="float:left; min-width:230px; padding-right:16px; "> + <label>Zoom:</label> + <p class="input-group"> + <select style="float:left; width:180px" class="form-control" ng-model="$parent.zoomTimespan" ng-options="option.name for option in $parent.zoomTimespans track by option.value" ng-change="$parent.onZoomTimespanChanged()"></select> + <button style="float:left; " title="Select full time window for this project" type="button" class="btn btn-default" ng-click="getFullTimeWindowForSelectedProject()"><i class="glyphicon glyphicon-resize-horizontal"></i></button> + </p> + </div> + <div style="float:left; min-width:50px;"> + <label>Disk usage:</label> + <p class="input-group"> + <button title="Show disk usage by project" type="button" class="btn btn-default" ng-click="cleanupCtrl.showAllProjectsDiskUsage()"><i class="glyphicon glyphicon-floppy-disk"></i></button> + </p> + </div> + </div> + + <div class="top-stretch" ui-layout options="{flow: 'column'}"> + <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" ui-layout-init-min-width="1160px"> + <div id="grid" + ui-grid="gridOptions" + ui-grid-edit ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-auto-resize + class="grid"></div> + </div> + </div> + </div> + {% endraw %} + </body> +</html> diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py index 221c4a040647301d59e0159405a130ba0f73ac53..2998ab6df1c0dc874387820d65e85d23872c5037 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py @@ -31,6 +31,7 @@ from threading import Condition from datetime import datetime import time import logging +import subprocess from dateutil import parser, tz from flask import Flask from flask import render_template @@ -41,7 +42,8 @@ from flask.json import jsonify from flask.json import JSONEncoder from lofar.sas.resourceassignment.resourceassignmenteditor.utils import gzipped from lofar.sas.resourceassignment.resourceassignmenteditor.fakedata import * -from lofar.sas.resourceassignment.resourceassignmenteditor.radbchangeshandler import RADBChangesHandler, CHANGE_DELETE_TYPE +from lofar.sas.resourceassignment.resourceassignmenteditor.changeshandler import ChangesHandler, CHANGE_DELETE_TYPE +from lofar.sas.datamanagement.common.config import DEFAULT_DM_NOTIFICATION_BUSNAME, DEFAULT_DM_NOTIFICATION_SUBJECTS from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_BUSNAME as DEFAULT_RADB_CHANGES_BUSNAME from lofar.sas.resourceassignment.database.config import DEFAULT_NOTIFICATION_SUBJECTS as DEFAULT_RADB_CHANGES_SUBJECTS from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC @@ -49,8 +51,20 @@ from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAUL from lofar.sas.resourceassignment.resourceassignmentservice.config import DEFAULT_SERVICENAME as DEFAULT_RADB_SERVICENAME from lofar.mom.momqueryservice.momqueryrpc import MoMQueryRPC from lofar.mom.momqueryservice.config import DEFAULT_MOMQUERY_BUSNAME, DEFAULT_MOMQUERY_SERVICENAME +from lofar.mom.momqueryservice.momrpc import MoMRPC +from lofar.mom.momqueryservice.config import DEFAULT_MOM_BUSNAME, DEFAULT_MOM_SERVICENAME from lofar.sas.resourceassignment.resourceassignmenteditor.mom import updateTaskMomDetails -#from lofar.sas.resourceassignment.resourceassigner. import updateTaskMomDetails +from lofar.sas.resourceassignment.resourceassignmenteditor.storage import updateTaskStorageDetails +from lofar.sas.datamanagement.cleanup.rpc import CleanupRPC +from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME as DEFAULT_CLEANUP_BUSNAME +from lofar.sas.datamanagement.cleanup.config import DEFAULT_SERVICENAME as DEFAULT_CLEANUP_SERVICENAME +from lofar.sas.datamanagement.storagequery.rpc import StorageQueryRPC +from lofar.sas.datamanagement.storagequery.config import DEFAULT_BUSNAME as DEFAULT_STORAGEQUERY_BUSNAME +from lofar.sas.datamanagement.storagequery.config import DEFAULT_SERVICENAME as DEFAULT_STORAGEQUERY_SERVICENAME +from lofar.sas.otdb.otdbrpc import OTDBRPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME +from lofar.common import isProductionEnvironment, isTestEnvironment +from lofar.common.util import humanreadablesize logger = logging.getLogger(__name__) @@ -93,15 +107,43 @@ app.json_encoder = CustomJSONEncoder rarpc = None momrpc = None -radbchangeshandler = None +otdbrpc = None +curpc = None +sqrpc = None +momqueryrpc = None +changeshandler = None @app.route('/') @app.route('/index.htm') @app.route('/index.html') +@gzipped def index(): '''Serves the ResourceAssignmentEditor's index page''' return render_template('index.html', title='Scheduler') +@app.route('/projects') +@app.route('/projects.htm') +@app.route('/projects.html') +@gzipped +def projects(): + return render_template('projects.html', title='Projects') + +@app.route('/rest/config') +@gzipped +def config(): + config = {'mom_base_url':'', + 'lta_base_url':'', + 'inspection_plots_base_url':'https://proxy.lofar.eu/inspect/HTML/'} + + if isProductionEnvironment(): + config['mom_base_url'] = 'https://lofar.astron.nl/mom3' + config['lta_base_url'] = 'http://lofar.target.rug.nl/' + elif isTestEnvironment(): + config['mom_base_url'] = 'http://lofartest.control.lofar:8080/mom3' + config['lta_base_url'] = 'http://lofar-test.target.rug.nl/' + + return jsonify({'config': config}) + @app.route('/rest/resources') @gzipped def resources(): @@ -159,6 +201,12 @@ def resourceUsagesForTask(task_id): result = rarpc.getResourceUsages(task_ids=[task_id]) return jsonify({'resourceusages': result}) +@app.route('/rest/tasks/<int:task_id>/resourceclaims', methods=['GET']) +@gzipped +def resourceClaimsForTask(task_id): + result = rarpc.getResourceClaims(task_ids=[task_id], extended=True, include_properties=True) + return jsonify({'resourceclaims': result}) + @app.route('/rest/tasks') def getTasks(): return getTasksFromUntil(None, None) @@ -178,14 +226,13 @@ def getTasksFromUntil(fromTimestamp=None, untilTimestamp=None): tasks = rarpc.getTasks(fromTimestamp, untilTimestamp) - # there are no task names in the database yet. - # will they come from spec/MoM? - # add Task <id> as name for now - updateTaskMomDetails(tasks, momrpc) + updateTaskMomDetails(tasks, momqueryrpc) + updateTaskStorageDetails(tasks, sqrpc) return jsonify({'tasks': tasks}) @app.route('/rest/tasks/<int:task_id>', methods=['GET']) +@gzipped def getTask(task_id): try: task = rarpc.getTask(task_id) @@ -194,17 +241,80 @@ def getTask(task_id): abort(404) task['name'] = 'Task %d' % task['id'] - updateTaskMomDetails(task, momrpc) + updateTaskMomDetails(task, momqueryrpc) + updateTaskStorageDetails(task, sqrpc) + return jsonify({'task': task}) + except Exception as e: + abort(404) + + return jsonify({'task': None}) + +@app.route('/rest/tasks/otdb/<int:otdb_id>', methods=['GET']) +@gzipped +def getTaskByOTDBId(otdb_id): + try: + task = rarpc.getTask(otdb_id=otdb_id) + + if not task: + abort(404) + + task['name'] = 'Task %d' % task['id'] + updateTaskMomDetails(task, momqueryrpc) + updateTaskStorageDetails(task, sqrpc) return jsonify({'task': task}) except Exception as e: abort(404) return jsonify({'task': None}) +@app.route('/rest/tasks/mom/<int:mom_id>', methods=['GET']) +@gzipped +def getTaskByMoMId(mom_id): + try: + task = rarpc.getTask(mom_id=mom_id) + + if not task: + abort(404) + + task['name'] = 'Task %d' % task['id'] + updateTaskMomDetails(task, momqueryrpc) + updateTaskStorageDetails(task, sqrpc) + return jsonify({'task': task}) + except Exception as e: + abort(404) + + return jsonify({'task': None}) + +@app.route('/rest/tasks/mom/group/<int:mom_group_id>', methods=['GET']) +@gzipped +def getTasksByMoMGroupId(mom_group_id): + try: + mom_ids = momqueryrpc.getTaskIdsInGroup(mom_group_id)[str(mom_group_id)] + tasks = rarpc.getTasks(mom_ids=mom_ids) + + updateTaskMomDetails(tasks, momqueryrpc) + updateTaskStorageDetails(tasks, sqrpc) + + return jsonify({'tasks': tasks}) + except Exception as e: + abort(404) + +@app.route('/rest/tasks/mom/parentgroup/<int:mom_parent_group_id>', methods=['GET']) +@gzipped +def getTasksByMoMParentGroupId(mom_parent_group_id): + try: + mom_ids = momqueryrpc.getTaskIdsInParentGroup(mom_parent_group_id)[str(mom_parent_group_id)] + tasks = rarpc.getTasks(mom_ids=mom_ids) + + updateTaskMomDetails(tasks, momqueryrpc) + updateTaskStorageDetails(tasks, sqrpc) + + return jsonify({'tasks': tasks}) + except Exception as e: + abort(404) + @app.route('/rest/tasks/<int:task_id>', methods=['PUT']) def putTask(task_id): - abort(403, 'Editing of tasks is by users is not yet approved') - if 'Content-Type' in request.headers and \ request.headers['Content-Type'].startswith('application/json'): updatedTask = json.loads(request.data) @@ -214,61 +324,170 @@ def putTask(task_id): abort(404) if 'starttime' in updatedTask: + if isProductionEnvironment(): + abort(403, 'Editing of startime of tasks by users is not yet approved') + try: updatedTask['starttime'] = asDatetime(updatedTask['starttime']) except ValueError: abort(400, 'timestamp not in iso format: ' + updatedTask['starttime']) if 'endtime' in updatedTask: + if isProductionEnvironment(): + abort(403, 'Editing of endtime of tasks by users is not yet approved') + try: updatedTask['endtime'] = asDatetime(updatedTask['endtime']) except ValueError: abort(400, 'timestamp not in iso format: ' + updatedTask['endtime']) logger.info('putTask: ' + str(updatedTask)) - rarpc.updateTaskAndResourceClaims(task_id, - starttime=updatedTask.get('starttime', None), - endtime=updatedTask.get('endtime', None), - task_status=updatedTask.get('status', None)) + if 'status' in updatedTask: + task = rarpc.getTask(task_id) + + if not task: + abort(404, "unknown task %s" % str(updatedTask)) + + otdbrpc.taskSetStatus(task['otdb_id'], updatedTask['status']) + + #rarpc.updateTaskAndResourceClaims(task_id, + #starttime=updatedTask.get('starttime', None), + #endtime=updatedTask.get('endtime', None)) return "", 204 except KeyError: abort(404) abort(406) +@app.route('/rest/tasks/<int:task_id>/cleanup', methods=['DELETE']) +def cleanupTaskData(task_id): + try: + delete_params = {} + + if 'Content-Type' in request.headers and (request.headers['Content-Type'].startswith('application/json') or request.headers['Content-Type'].startswith('text/plain')): + delete_params = json.loads(request.data) + + task = rarpc.getTask(task_id) + + if not task: + abort(404, 'No such task (id=%s)' % task_id) + + logger.info("cleanup task data id=%s otdb_id=%s delete_params=%s", task_id, task['otdb_id'], delete_params) + + result = curpc.removeTaskData(task['otdb_id'], + delete_is=delete_params.get('delete_is', True), + delete_cs=delete_params.get('delete_cs', True), + delete_uv=delete_params.get('delete_uv', True), + delete_im=delete_params.get('delete_im', True), + delete_img=delete_params.get('delete_img', True), + delete_pulp=delete_params.get('delete_pulp', True), + delete_scratch=delete_params.get('delete_scratch', True)) + logger.info(result) + return jsonify(result) + except Exception as e: + abort(500) + +@app.route('/rest/tasks/<int:task_id>/datapath', methods=['GET']) +@gzipped +def getTaskDataPath(task_id): + try: + task = rarpc.getTask(task_id) + + if not task: + abort(404, 'No such task (id=%s)' % task_id) + + result = sqrpc.getPathForOTDBId(task['otdb_id']) + except Exception as e: + abort(500, str(e)) + + if result['found']: + return jsonify({'datapath': result['path']}) + abort(404, result['message'] if result and 'message' in result else '') + +@app.route('/rest/tasks/otdb/<int:otdb_id>/diskusage', methods=['GET']) +@gzipped +def getTaskDiskUsageByOTDBId(otdb_id): + try: + result = sqrpc.getDiskUsageForTaskAndSubDirectories(otdb_id=otdb_id) + except Exception as e: + abort(500, str(e)) + + if result['found']: + return jsonify(result) + abort(404, result['message'] if result and 'message' in result else '') + +@app.route('/rest/tasks/<int:task_id>/diskusage', methods=['GET']) +@gzipped +def getTaskDiskUsage(task_id): + try: + result = sqrpc.getDiskUsageForTaskAndSubDirectories(radb_id=task_id) + except Exception as e: + abort(500, str(e)) + + if result['found']: + return jsonify(result) + abort(404, result['message'] if result and 'message' in result else '') + +@app.route('/rest/tasks/<int:task_id>/copy', methods=['PUT']) +def copyTask(task_id): + if isProductionEnvironment(): + abort(403, 'Copying of tasks is by users is not yet approved') + + try: + task = rarpc.getTask(task_id) + + if not task: + logger.error('Could not find task %s' % task_id) + abort(404, 'Could not find task %s' % task_id) + + mom2id = task['mom_id'] + new_task_mom2id = momrpc.copyTask(mom2id=mom2id) + return jsonify({'copied':True, 'new_task_mom2id':new_task_mom2id, 'old_task_mom2id':mom2id}) + except Exception as e: + logger.error(e) + abort(404, str(e)) + + return jsonify({'copied':False}) + @app.route('/rest/tasks/<int:task_id>/resourceclaims') +@gzipped def taskResourceClaims(task_id): return jsonify({'taskResourceClaims': rarpc.getResourceClaims(task_id=task_id, include_properties=True)}) @app.route('/rest/tasktypes') +@gzipped def tasktypes(): result = rarpc.getTaskTypes() result = sorted(result, key=lambda q: q['id']) return jsonify({'tasktypes': result}) @app.route('/rest/taskstatustypes') +@gzipped def getTaskStatusTypes(): result = rarpc.getTaskStatuses() result = sorted(result, key=lambda q: q['id']) return jsonify({'taskstatustypes': result}) @app.route('/rest/resourcetypes') +@gzipped def resourcetypes(): result = rarpc.getResourceTypes() result = sorted(result, key=lambda q: q['id']) return jsonify({'resourcetypes': result}) @app.route('/rest/resourceclaimpropertytypes') +@gzipped def resourceclaimpropertytypes(): result = rarpc.getResourceClaimPropertyTypes() result = sorted(result, key=lambda q: q['id']) return jsonify({'resourceclaimpropertytypes': result}) -@app.route('/rest/momprojects') -def getMoMProjects(): +@app.route('/rest/projects') +@gzipped +def getProjects(): projects = [] try: - projects = momrpc.getProjects() + projects = momqueryrpc.getProjects() projects = [x for x in projects if x['status_id'] in [1, 7]] for project in projects: project['mom_id'] = project.pop('mom2id') @@ -279,9 +498,99 @@ def getMoMProjects(): projects.append({'name':'OTDB Only', 'mom_id':-98, 'description': 'Container project for tasks which exists only in OTDB'}) return jsonify({'momprojects': projects}) +@app.route('/rest/projects/<int:project_mom2id>') +@gzipped +def getProject(project_mom2id): + try: + projects = momqueryrpc.getProjects() + project = next(x for x in projects if x['mom2id'] == project_mom2id) + return jsonify({'momproject': project}) + except StopIteration as e: + logger.error(e) + abort(404, "No project with mom2id %s" % project_mom2id) + except Exception as e: + logger.error(e) + abort(404, str(e)) + +@app.route('/rest/projects/<int:project_mom2id>/tasks') +@gzipped +def getProjectTasks(project_mom2id): + return getProjectTasksFromUntil(project_mom2id, None, None) + +@app.route('/rest/projects/<int:project_mom2id>/tasks/<string:fromTimestamp>/<string:untilTimestamp>') +@gzipped +def getProjectTasksFromUntil(project_mom2id, fromTimestamp=None, untilTimestamp=None): + try: + if fromTimestamp and isinstance(fromTimestamp, basestring): + fromTimestamp = asDatetime(fromTimestamp) + + if untilTimestamp and isinstance(untilTimestamp, basestring): + untilTimestamp = asDatetime(untilTimestamp) + + task_mom2ids = momqueryrpc.getProjectTaskIds(project_mom2id)['task_mom2ids'] + + tasks = rarpc.getTasks(mom_ids=task_mom2ids, lower_bound=fromTimestamp, upper_bound=untilTimestamp) + + updateTaskMomDetails(tasks, momqueryrpc) + updateTaskStorageDetails(tasks, sqrpc) + + return jsonify({'tasks': tasks}) + except Exception as e: + logger.error(e) + abort(404, str(e)) + +@app.route('/rest/projects/<int:project_mom2id>/taskstimewindow') +@gzipped +def getProjectTasksTimeWindow(project_mom2id): + try: + return jsonify({'min_starttime': datetime(2016, 01, 01), 'max_endtime': datetime(2016, 12, 01)}) + + task_mom2ids = momqueryrpc.getProjectTaskIds(project_mom2id)['task_mom2ids'] + + timewindow = rarpc.getTasksTimeWindow(mom_ids=task_mom2ids) + + return jsonify(timewindow) + except Exception as e: + logger.error(e) + abort(404, str(e)) + +@app.route('/rest/projects/<int:project_mom2id>/diskusage') +@gzipped +def getProjectDiskUsageById(project_mom2id): + try: + project = momqueryrpc.getProject(project_mom2id=project_mom2id) + return getProjectDiskUsageByName(project['name']) + except StopIteration as e: + logger.error(e) + abort(404, "No project with mom2id %s" % project_mom2id) + except Exception as e: + logger.error(e) + abort(404, str(e)) + +@app.route('/rest/projects/<string:project_name>/diskusage') +@gzipped +def getProjectDiskUsageByName(project_name): + try: + result = sqrpc.getDiskUsageForProjectDirAndSubDirectories(project_name=project_name) + return jsonify(result) + except Exception as e: + logger.error(e) + abort(404, str(e)) + +@app.route('/rest/projects/diskusage') +@gzipped +def getProjectsDiskUsage(): + try: + result = sqrpc.getDiskUsageForProjectsDirAndSubDirectories() + return jsonify(result) + except Exception as e: + logger.error(e) + abort(404, str(e)) + @app.route('/rest/momobjectdetails/<int:mom2id>') +@gzipped def getMoMObjectDetails(mom2id): - details = momrpc.getProjectDetails(mom2id) + details = momqueryrpc.getProjectDetails(mom2id) details = details.values()[0] if details else None if details: details['project_mom_id'] = details.pop('project_mom2id') @@ -290,13 +599,15 @@ def getMoMObjectDetails(mom2id): return jsonify({'momobjectdetails': details}) @app.route('/rest/updates/<int:sinceChangeNumber>') +@gzipped def getUpdateEventsSince(sinceChangeNumber): - changesSince = radbchangeshandler.getChangesSince(sinceChangeNumber) + changesSince = changeshandler.getChangesSince(sinceChangeNumber) return jsonify({'changes': changesSince}) @app.route('/rest/mostRecentChangeNumber') +@gzipped def getMostRecentChangeNumber(): - mrcn = radbchangeshandler.getMostRecentChangeNumber() + mrcn = changeshandler.getMostRecentChangeNumber() return jsonify({'mostRecentChangeNumber': mrcn}) @app.route('/rest/updates') @@ -304,9 +615,165 @@ def getUpdateEvents(): return getUpdateEventsSince(-1L) @app.route('/rest/lofarTime') +@gzipped def getLofarTime(): return jsonify({'lofarTime': asIsoFormat(datetime.utcnow())}) + +#ugly method to generate html tables for all tasks +@app.route('/tasks.html') +@gzipped +def getTasksHtml(): + tasks = rarpc.getTasks() + if not tasks: + abort(404) + + updateTaskMomDetails(tasks, momqueryrpc) + updateTaskStorageDetails(tasks, sqrpc) + + html = '<!DOCTYPE html><html><head><title>Tasks</title><style>table, th, td {border: 1px solid black; border-collapse: collapse; padding: 4px;}</style></head><body><table style="width:100%">\n' + + props = sorted(tasks[0].keys()) + html += '<tr>%s</tr>\n' % ''.join('<th>%s</th>' % prop for prop in props) + + for task in tasks: + html += '<tr>' + for prop in props: + if prop in task: + if prop == 'id': + html += '<td><a href="/rest/tasks/%s.html">%s</a></td> ' % (task[prop], task[prop]) + else: + html += '<td>%s</td> ' % task[prop] + html += '</tr>\n' + + html += '</table></body></html>\n' + + return html + +#ugly method to generate html tables for the task and it's claims +@app.route('/tasks/<int:task_id>.html', methods=['GET']) +@gzipped +def getTaskHtml(task_id): + task = rarpc.getTask(task_id) + + if not task: + abort(404, 'No such task %s' % task_id) + + task['name'] = 'Task %d' % task['id'] + updateTaskMomDetails(task, momqueryrpc) + updateTaskStorageDetails(task, sqrpc) + + html = '<!DOCTYPE html><html><head><title>Tasks</title><style>table, th, td {border: 1px solid black; border-collapse: collapse; padding: 4px;}</style></head><body><table style="">\n' + + html += '<h1>Task %s</h1>' % task_id + + html += '<p><a href="/tasks/%s/log.html">%s log</a></p> ' % (task['id'], task['type']) + + props = sorted(task.keys()) + html += '<tr><th>key</th><th>value</th></tr>\n' + + for prop in props: + html += '<tr><td>%s</td>' % prop + + if prop == 'id': + html += '<td><a href="/tasks/%s.html">%s</a></td> ' % (task[prop], task[prop]) + elif prop == 'predecessor_ids' or prop == 'successor_ids': + ids = task[prop] + if ids: + html += '<td>%s</td> ' % ', '.join('<a href="/tasks/%s.html">%s</a>' % (id, id) for id in ids) + else: + html += '<td></td> ' + else: + html += '<td>%s</td> ' % task[prop] + + html += '</tr>' + + html += '</table>\n<br>' + + claims = rarpc.getResourceClaims(task_ids=[task_id], extended=True, include_properties=True) + + if claims: + html += '<h1>Claims</h1>' + + for claim in claims: + html += '<table>' + for claim_key,claim_value in claim.items(): + if claim_key == 'properties': + html += '<tr><td>properties</td><td><table>' + if claim_value: + propnames = sorted(claim_value[0].keys()) + html += '<tr>%s</tr>\n' % ''.join('<th>%s</th>' % propname for propname in propnames) + for prop in claim_value: + html += '<tr>%s</tr>\n' % ''.join('<td>%s</td>' % prop[propname] for propname in propnames) + html += '</table></td></tr>' + elif claim_key == 'saps': + html += '<tr><td>saps</td><td><table>' + saps = claim_value + if saps: + sap_keys = ['sap_nr', 'properties'] + html += '<tr>%s</tr>\n' % ''.join('<th>%s</th>' % sap_key for sap_key in sap_keys) + for sap in saps: + html += '<tr>' + for sap_key in sap_keys: + if sap_key == 'properties': + html += '<td><table>' + sap_props = sap[sap_key] + if sap_props: + propnames = sorted(sap_props[0].keys()) + html += '<tr>%s</tr>\n' % ''.join('<th>%s</th>' % propname for propname in propnames) + for prop in sap_props: + html += '<tr>%s</tr>\n' % ''.join('<td>%s</td>' % prop[propname] for propname in propnames) + html += '</table></td>' + else: + html += '<td>%s</td>' % (sap[sap_key]) + html += '</tr>' + html += '</table></td></tr>' + else: + html += '<tr><td>%s</td><td>%s</td></tr>' % (claim_key,claim_value) + html += '</table>' + html += '<br>' + + html += '</body></html>\n' + + return html + +@app.route('/rest/tasks/<int:task_id>/resourceclaims.html', methods=['GET']) +@gzipped +def resourceClaimsForTaskHtml(task_id): + claims = rarpc.getResourceClaims(task_ids=[task_id], extended=True, include_properties=True) + + if not claims: + abort(404, 'No resource claims for task %s' % task_id) + + html = '<!DOCTYPE html><html><head><title>Tasks</title><style>table, th, td {border: 1px solid black; border-collapse: collapse; padding: 4px;}</style></head><body><table style="">\n' + + for claim in claims: + html += '<tr><td>%s</td>' % claim + + html += '</table></body></html>\n' + + return html + +@app.route('/tasks/<int:task_id>/log.html', methods=['GET']) +@gzipped +def getTaskLogHtml(task_id): + task = rarpc.getTask(task_id) + + cmd = [] + if task['type'] == 'pipeline': + cmd = ['ssh', 'lofarsys@head01.cep4.control.lofar', 'cat /data/log/pipeline-%s-*.log' % task['otdb_id']] + else: + cmd = ['ssh', 'mcu001.control.lofar', 'cat /opt/lofar/var/log/MCU001\\:ObservationControl\\[0\\]\\{%s\\}.log*' % task['otdb_id']] + + logger.info(' '.join(cmd)) + + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + return out, 200, {'Content-Type': 'text/plain; charset=utf-8'} + else: + return err, 500, {'Content-Type': 'text/plain; charset=utf-8'} + def main(): # make sure we run in UTC timezone import os @@ -321,8 +788,19 @@ def main(): parser.add_option('--radb_servicename', dest='radb_servicename', type='string', default=DEFAULT_RADB_SERVICENAME, help='Name of the radbservice, default: %default') parser.add_option('--radb_notification_busname', dest='radb_notification_busname', type='string', default=DEFAULT_RADB_CHANGES_BUSNAME, help='Name of the notification bus exchange on the qpid broker on which the radb notifications are published, default: %default') parser.add_option('--radb_notification_subjects', dest='radb_notification_subjects', type='string', default=DEFAULT_RADB_CHANGES_SUBJECTS, help='Subject(s) to listen for on the radb notification bus exchange on the qpid broker, default: %default') - parser.add_option('--mom_busname', dest='mom_busname', type='string', default=DEFAULT_MOMQUERY_BUSNAME, help='Name of the bus exchange on the qpid broker on which the momservice listens, default: %default') - parser.add_option('--mom_servicename', dest='mom_servicename', type='string', default=DEFAULT_MOMQUERY_SERVICENAME, help='Name of the momservice, default: %default') + parser.add_option('--otdb_busname', dest='otdb_busname', type='string', default=DEFAULT_OTDB_SERVICE_BUSNAME, help='Name of the bus exchange on the qpid broker on which the otdbservice listens, default: %default') + parser.add_option('--otdb_servicename', dest='otdb_servicename', type='string', default=DEFAULT_OTDB_SERVICENAME, help='Name of the otdbservice, default: %default') + parser.add_option('--mom_busname', dest='mom_busname', type='string', default=DEFAULT_MOM_BUSNAME, help='Name of the bus exchange on the qpid broker on which the momservice listens, default: %default') + parser.add_option('--mom_servicename', dest='mom_servicename', type='string', default=DEFAULT_MOM_SERVICENAME, help='Name of the momservice, default: %default') + parser.add_option('--mom_broker', dest='mom_broker', type='string', default=None, help='Address of the qpid broker for the mom service, default: localhost') + parser.add_option('--mom_query_busname', dest='mom_query_busname', type='string', default=DEFAULT_MOMQUERY_BUSNAME, help='Name of the bus exchange on the qpid broker on which the momqueryservice listens, default: %default') + parser.add_option('--mom_query_servicename', dest='mom_query_servicename', type='string', default=DEFAULT_MOMQUERY_SERVICENAME, help='Name of the momqueryservice, default: %default') + parser.add_option('--cleanup_busname', dest='cleanup_busname', type='string', default=DEFAULT_CLEANUP_BUSNAME, help='Name of the bus exchange on the qpid broker on which the cleanupservice listens, default: %default') + parser.add_option('--cleanup_servicename', dest='cleanup_servicename', type='string', default=DEFAULT_CLEANUP_SERVICENAME, help='Name of the cleanupservice, default: %default') + parser.add_option('--storagequery_busname', dest='storagequery_busname', type='string', default=DEFAULT_STORAGEQUERY_BUSNAME, help='Name of the bus exchange on the qpid broker on which the storagequeryservice listens, default: %default') + parser.add_option('--storagequery_servicename', dest='storagequery_servicename', type='string', default=DEFAULT_STORAGEQUERY_SERVICENAME, help='Name of the storagequeryservice, default: %default') + parser.add_option('--dm_notification_busname', dest='dm_notification_busname', type='string', default=DEFAULT_DM_NOTIFICATION_BUSNAME, help='Name of the notification bus exchange on the qpid broker on which the data management notifications are published, default: %default') + parser.add_option('--dm_notification_subjects', dest='dm_notification_subjects', type='string', default=DEFAULT_DM_NOTIFICATION_SUBJECTS, help='Subject(s) to listen for on the data management notification bus exchange on the qpid broker, default: %default') parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') (options, args) = parser.parse_args() @@ -330,13 +808,23 @@ def main(): level=logging.DEBUG if options.verbose else logging.INFO) global rarpc - rarpc = RARPC(busname=DEFAULT_RADB_BUSNAME, servicename=DEFAULT_RADB_SERVICENAME, broker=options.broker) + rarpc = RARPC(busname=options.radb_busname, servicename=options.radb_servicename, broker=options.broker) global momrpc - momrpc = MoMQueryRPC(busname=DEFAULT_MOMQUERY_BUSNAME, servicename=DEFAULT_MOMQUERY_SERVICENAME, timeout=2.5, broker=options.broker) - global radbchangeshandler - radbchangeshandler = RADBChangesHandler(DEFAULT_RADB_CHANGES_BUSNAME, broker=options.broker, momrpc=momrpc) - - with radbchangeshandler, rarpc, momrpc: + momrpc = MoMRPC(busname=options.mom_busname, servicename=options.mom_servicename, timeout=10, broker=options.broker) + global otdbrpc + otdbrpc = OTDBRPC(busname=options.otdb_busname, servicename=options.otdb_servicename, broker=options.broker) + global curpc + curpc = CleanupRPC(busname=options.cleanup_busname, servicename=options.cleanup_servicename, broker=options.broker) + global sqrpc + sqrpc = StorageQueryRPC(busname=options.storagequery_busname, servicename=options.storagequery_servicename, broker=options.broker) + global momqueryrpc + momqueryrpc = MoMQueryRPC(busname=options.mom_query_busname, servicename=options.mom_query_servicename, timeout=10, broker=options.broker) + global changeshandler + changeshandler = ChangesHandler(radb_busname=options.radb_notification_busname, radb_subjects=options.radb_notification_subjects, + dm_busname=options.dm_notification_busname, dm_subjects=options.dm_notification_subjects, + broker=options.broker, momqueryrpc=momqueryrpc, radbrpc=rarpc) + + with changeshandler, rarpc, otdbrpc, curpc, sqrpc, momrpc, momqueryrpc: '''Start the webserver''' app.run(debug=options.verbose, threaded=True, host='0.0.0.0', port=options.port) diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/test/test_webservice.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/test/test_webservice.py index 17568953a73dcef305a7041c55681b31ab052f96..9209f02fe51474c017fbdf6bd9d09ea071817b75 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/test/test_webservice.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/test/test_webservice.py @@ -114,7 +114,7 @@ class TestLiveResourceAssignmentEditor(FlaskLiveTestCase): def main(argv): - unittest.main(verbosity=2) + unittest.main() # run all tests if main if __name__ == '__main__': diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice.ini b/SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice.ini index 49a714aedeb4fafed6080c9da073231659805838..b80c27c80b51a32bee33dcf3d9c760e5d8459e4b 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice.ini +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/raestimatorservice.ini @@ -1,5 +1,5 @@ [program:raestimatorservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;raestimatorservice' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec raestimatorservice' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/CMakeLists.txt index 6c42b9e1eede3045170add0188d7a87067fd430c..ccfd9a6d16ac41a95dc940cf5499435fa04bb871 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/CMakeLists.txt @@ -3,6 +3,7 @@ set(_py_files __init__.py base_resource_estimator.py + base_pipeline_estimator.py observation.py longbaseline_pipeline.py calibration_pipeline.py diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_pipeline_estimator.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_pipeline_estimator.py new file mode 100644 index 0000000000000000000000000000000000000000..1d666829ccbf019353c3955a1d9e600bd7d92b26 --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_pipeline_estimator.py @@ -0,0 +1,51 @@ +# base_resource_estimator.py +# +# Copyright (C) 2016 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id: base_resource_estimator.py 33534 2016-02-08 14:28:26Z schaap $ + +import logging +import pprint +from math import ceil +from base_resource_estimator import BaseResourceEstimator + +logger = logging.getLogger(__name__) + +DATAPRODUCTS = "Observation.DataProducts." +PIPELINE = "Observation.ObservationControl.PythonControl." + +#Observation.DataProducts.Output_Correlated.storageClusterName= + +class BasePipelineResourceEstimator(BaseResourceEstimator): + """ base ResourceEstimator for all Pipelines + """ + def __init__(self, name): + logger.info("init BasePipelineResourceEstimator") + super(BasePipelineResourceEstimator, self).__init__(name=name) + + def _getDuration(self, start, end): + # pipelines could be prescheduled for the resource assigner + # without a proper start/end time + # just return a default duration in that case, because slurm will handle the start/end time + try: + return super(BasePipelineResourceEstimator, self)._getDuration(start, end) + except Exception as e: + logger.error(e) + logger.info("Could not get duration from parset, returning default pipeline duration of 1 hour") + return 3600 diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_resource_estimator.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_resource_estimator.py index 45b18dc87c7d762be6509a695284bba48e4dc4ce..59fe0bbc049aa1074a3b2897d5a748fa6b377671 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_resource_estimator.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/base_resource_estimator.py @@ -23,6 +23,8 @@ """ Base class for Resource Estimators """ import logging +import copy +import pprint from datetime import datetime from lofar.common.datetimeutils import totalSeconds from datetime import datetime, timedelta @@ -47,7 +49,7 @@ class BaseResourceEstimator(object): logger.error("missing keys: %s" % ', '.join(missing_keys)) return False return True - + def _getDateTime(self, date_time): return datetime.strptime(date_time, '%Y-%m-%d %H:%M:%S') @@ -63,6 +65,48 @@ class BaseResourceEstimator(object): def _calculate(self, parset, input_files={}): raise NotImplementedError('calculate() in base class is called. Please implement calculate() in your subclass') + def _filterInputs(self, input_files, identifications): + """'input_files': + {'uv': {'nr_of_uv_files': 481, 'uv_file_size': 1482951104, + 'identifications':['mom.G355415.B2.1.C.SAP000.uv.dps','mom.G355415.B2.1.C.SAP001.uv.dps','mom.G355415.B2.1.C.SAP002.uv.dps']}, + 'saps': [{'sap_nr': 0, 'properties': {'nr_of_uv_files': 319}}, + {'sap_nr': 1, 'properties': {'nr_of_uv_files': 81}}, + {'sap_nr': 2, 'properties': {'nr_of_uv_files': 81}} + ]}}} + 'identifications': 'mom.G355415.B2.1.C.SAP000.uv.dps','mom.G355415.B2.1.C.SAP001.uv.dps' + """ + output_files = {} + if 'saps' in input_files: + output_files['saps'] = [] + logger.info('parsing input for identifications: %s' % (identifications,)) + for identification in identifications: + for data_type, data_properties in input_files.items(): + if data_type == 'saps': + continue + if identification in data_properties['identifications']: #This data_type matches an input identification + logger.info('Found input identification matching %s' % (identification,)) + output_files[data_type] = copy.deepcopy(data_properties) + if 'SAP' in identification: #Special case for identifications that contain a SAP number + # We would need to make this smarter if we can have the data from multiple SAPs as input. + # Or or different input sources of the same data_type + for s in identification.split('.'): # Find the SAP number + if 'SAP' in s: + try: + sap_nr = int(s[3:]) + except: + sap_nr = None + for sap in input_files['saps']: + if sap['sap_nr'] == sap_nr: + for sap_data_type, sap_data_value in sap['properties'].items(): + if sap_data_type in data_properties: # We found this SAP's nr_of_<data_type>_files + output_files[data_type][sap_data_type] = sap_data_value # We only count the nr_of_files from this SAP + output_files['saps'].append({'sap_nr': sap_nr, 'properties': sap['properties']}) + if sap_data_type == 'start_sb_nr': + output_files[data_type]['start_sb_nr'] = sap_data_value + logger.info('filtered input files down to: \n' + pprint.pformat(output_files)) + return output_files + + def verify_and_estimate(self, parset, input_files={}): """ Create estimates for a single process based on its parset and input files""" if self._checkParsetForRequiredKeys(parset): @@ -70,7 +114,7 @@ class BaseResourceEstimator(object): else: raise ValueError('The parset is incomplete') result = {} - result[self.name] = {} - result[self.name]['storage'] = estimates['storage'] - result[self.name]['bandwidth'] = estimates['bandwidth'] + result[self.name] = estimates + logger.info('Estimates for %s:' % self.name) + logger.info(pprint.pformat(result)) return result diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/calibration_pipeline.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/calibration_pipeline.py index 0d4167e8b1014f05ee4a588ba1fd0d072fe0976c..fe87a4eab80fd24f8324bab0de0fa4ab7703be20 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/calibration_pipeline.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/calibration_pipeline.py @@ -21,8 +21,9 @@ # $Id: base_resource_estimator.py 33534 2016-02-08 14:28:26Z schaap $ import logging +import pprint from math import ceil -from base_resource_estimator import BaseResourceEstimator +from base_pipeline_estimator import BasePipelineResourceEstimator logger = logging.getLogger(__name__) @@ -31,17 +32,20 @@ PIPELINE = "Observation.ObservationControl.PythonControl." #Observation.DataProducts.Output_Correlated.storageClusterName= -class CalibrationPipelineResourceEstimator(BaseResourceEstimator): +class CalibrationPipelineResourceEstimator(BasePipelineResourceEstimator): """ ResourceEstimator for Calibration Pipelines """ def __init__(self): logger.info("init CalibrationPipelineResourceEstimator") - BaseResourceEstimator.__init__(self, name='calibration_pipeline') + BasePipelineResourceEstimator.__init__(self, name='pipeline') #FIXME name='calibration_pipeline' self.required_keys = ('Observation.startTime', 'Observation.stopTime', DATAPRODUCTS + 'Input_Correlated.enabled', + DATAPRODUCTS + 'Input_Correlated.identifications', DATAPRODUCTS + 'Output_InstrumentModel.enabled', + DATAPRODUCTS + 'Output_InstrumentModel.identifications', DATAPRODUCTS + 'Output_Correlated.enabled', + DATAPRODUCTS + 'Output_Correlated.identifications', PIPELINE + 'DPPP.demixer.freqstep', PIPELINE + 'DPPP.demixer.timestep') @@ -61,8 +65,13 @@ class CalibrationPipelineResourceEstimator(BaseResourceEstimator): }} """ logger.debug("start estimate '{}'".format(self.name)) - logger.info('parset: %s ' % parset) - result = {'errors': []} + logger.info('parset:\n%s' % (parset,)) + logger.info('input_files: \n' + pprint.pformat(input_files)) + result = {'errors': [], 'storage': {'total_size': 0}, 'bandwidth': {'total_size': 0}} + identifications = parset.getStringVector(DATAPRODUCTS + 'Input_Correlated.identifications') + parset.getStringVector(DATAPRODUCTS + 'Input_InstrumentModel.identifications') + input_files = self._filterInputs(input_files, identifications) + result['storage']['input_files'] = input_files + duration = self._getDuration(parset.getString('Observation.startTime'), parset.getString('Observation.stopTime')) freq_step = parset.getInt(PIPELINE + 'DPPP.demixer.freqstep', 1) #TODO, should these have defaults? time_step = parset.getInt(PIPELINE + 'DPPP.demixer.timestep', 1) @@ -80,25 +89,40 @@ class CalibrationPipelineResourceEstimator(BaseResourceEstimator): if result['errors']: return result - logger.debug("calculate correlated data size") - result['output_files'] = {} + logger.info("calculate correlated data size") + result['storage']['output_files'] = {} input_file_size = input_files['uv']['uv_file_size'] output_file_size = 0.0 new_size = input_file_size / float(reduction_factor) output_file_size = new_size + new_size / 64.0 * (1.0 + reduction_factor) + new_size / 2.0 - result['output_files']['uv'] = {'nr_of_uv_files': input_files['uv']['nr_of_uv_files'], 'uv_file_size': int(output_file_size)} - logger.debug("correlated_uv: {} files {} bytes each".format(result['output_files']['uv']['nr_of_uv_files'], result['output_files']['uv']['uv_file_size'])) + result['storage']['output_files']['uv'] = {'nr_of_uv_files': input_files['uv']['nr_of_uv_files'], + 'uv_file_size': int(output_file_size), + 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_Correlated.identifications'), + 'start_sb_nr': input_files['uv']['start_sb_nr']} + logger.info("correlated_uv: {} files {} bytes each".format(result['storage']['output_files']['uv']['nr_of_uv_files'], result['storage']['output_files']['uv']['uv_file_size'])) if parset.getBool(DATAPRODUCTS + 'Output_InstrumentModel.enabled'): - logger.debug("calculate instrument-model data size") - result['output_files']['im'] = {'nr_of_im_files': input_files['uv']['nr_of_uv_files'], 'im_file_size': 1000} # 1 kB was hardcoded in the Scheduler - logger.debug("correlated_uv: {} files {} bytes each".format(result['output_files']['im']['nr_of_im_files'], result['output_files']['im']['im_file_size'])) + logger.info("calculate instrument-model data size") + result['storage']['output_files']['im'] = {'nr_of_im_files': input_files['uv']['nr_of_uv_files'], + 'im_file_size': 1000, + 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_InstrumentModel.identifications'), + 'start_sb_nr': input_files['uv']['start_sb_nr']} # 1 kB was hardcoded in the Scheduler + logger.info("correlated_im: {} files {} bytes each".format(result['storage']['output_files']['im']['nr_of_im_files'], result['storage']['output_files']['im']['im_file_size'])) # count total data size - total_data_size = result['output_files']['uv']['nr_of_uv_files'] * result['output_files']['uv']['uv_file_size'] + \ - result['output_files']['im']['nr_of_im_files'] * result['output_files']['im']['im_file_size'] # bytes + total_data_size = 0 + for data_type in result['storage']['output_files']: + total_data_size += result['storage']['output_files'][data_type]['nr_of_' + data_type + '_files'] * result['storage']['output_files'][data_type][data_type + '_file_size'] # bytes + # FIXME I don't think this is technically correct, as the IM files are sometimes created and used, just not a required export? + # Need to split averaging pipeline and calibration pipeline + #total_data_size = result['storage']['output_files']['uv']['nr_of_uv_files'] * result['storage']['output_files']['uv']['uv_file_size'] + \ + # result['storage']['output_files']['im']['nr_of_im_files'] * result['storage']['output_files']['im']['im_file_size'] # bytes total_bandwidth = int(ceil((total_data_size * 8) / duration)) # bits/second - result['storage'] = {'total_size': total_data_size} - result['bandwidth'] = {'total_size': total_bandwidth} + if total_data_size: + result['storage']['total_size'] = total_data_size + result['bandwidth']['total_size'] = total_bandwidth + else: + result['errors'].append('Total data size is zero!') + logger.warning('ERROR: A datasize of zero was calculated!') return result diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/image_pipeline.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/image_pipeline.py index 2c4b60797efa5cbb7e54939135245fa7b14e8919..d09c30d9d1b89228a85e650b18b5b41a4895c78f 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/image_pipeline.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/image_pipeline.py @@ -22,7 +22,7 @@ import logging from math import ceil -from base_resource_estimator import BaseResourceEstimator +from base_pipeline_estimator import BasePipelineResourceEstimator from lofar.parameterset import parameterset logger = logging.getLogger(__name__) @@ -33,16 +33,18 @@ PIPELINE = "Observation.ObservationControl.PythonControl." #Observation.DataProducts.Output_Correlated.storageClusterName= #Observation.ObservationControl.PythonControl.AWimager -class ImagePipelineResourceEstimator(BaseResourceEstimator): +class ImagePipelineResourceEstimator(BasePipelineResourceEstimator): """ ResourceEstimator for Imaging Pipelines """ def __init__(self): logger.info("init ImagePipelineResourceEstimator") - BaseResourceEstimator.__init__(self, name='imaging_pipeline') + BasePipelineResourceEstimator.__init__(self, name='pipeline') #FIXME name='imaging_pipeline' self.required_keys = ('Observation.startTime', 'Observation.stopTime', DATAPRODUCTS + 'Input_Correlated.enabled', + DATAPRODUCTS + 'Input_Correlated.identifications', DATAPRODUCTS + 'Output_SkyImage.enabled', + DATAPRODUCTS + 'Output_SkyImage.identifications', PIPELINE + 'Imaging.slices_per_image', PIPELINE + 'Imaging.subbands_per_image') @@ -62,7 +64,10 @@ class ImagePipelineResourceEstimator(BaseResourceEstimator): """ logger.debug("start estimate '{}'".format(self.name)) logger.info('parset: %s ' % parset) - result = {'errors': []} + result = {'errors': [], 'storage': {'total_size': 0}, 'bandwidth': {'total_size': 0}} + input_files = self._filterInputs(input_files, parset.getStringVector(DATAPRODUCTS + 'Input_Correlated.identifications')) + result['storage']['input_files'] = input_files + duration = self._getDuration(parset.getString('Observation.startTime'), parset.getString('Observation.stopTime')) slices_per_image = parset.getInt(PIPELINE + 'Imaging.slices_per_image', 0) #TODO, should these have defaults? subbands_per_image = parset.getInt(PIPELINE + 'Imaging.subbands_per_image', 0) @@ -85,15 +90,19 @@ class ImagePipelineResourceEstimator(BaseResourceEstimator): return result logger.debug("calculate sky image data size") - result['output_files'] = {} + result['storage']['output_files'] = {} nr_images = nr_input_subbands / (subbands_per_image * slices_per_image) - result['output_files']['img'] = {'nr_of_img_files': nr_images, 'img_file_size': 1000} # 1 kB was hardcoded in the Scheduler - logger.debug("sky_images: {} files {} bytes each".format(result['output_files']['img']['nr_of_img_files'], result['output_files']['img']['img_file_size'])) + result['storage']['output_files']['img'] = {'nr_of_img_files': nr_images, 'img_file_size': 1000, 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_SkyImage.identifications')} # 1 kB was hardcoded in the Scheduler + logger.debug("sky_images: {} files {} bytes each".format(result['storage']['output_files']['img']['nr_of_img_files'], result['storage']['output_files']['img']['img_file_size'])) # count total data size - total_data_size = result['output_files']['img']['nr_of_img_files'] * result['output_files']['img']['img_file_size'] # bytes + total_data_size = result['storage']['output_files']['img']['nr_of_img_files'] * result['storage']['output_files']['img']['img_file_size'] # bytes total_bandwidth = int(ceil((total_data_size * 8) / duration)) # bits/second - result['storage'] = {'total_size': total_data_size} - result['bandwidth'] = {'total_size': total_bandwidth} + if total_data_size: + result['storage']['total_size'] = total_data_size + result['bandwidth']['total_size'] = total_bandwidth + else: + result['errors'].append('Total data size is zero!') + logger.warning('ERROR: A datasize of zero was calculated!') return result diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/longbaseline_pipeline.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/longbaseline_pipeline.py index c8d82defa440a099e25188c3ac4ba3b46343e052..100dc061d2b7659f9a17fa562bf5aaebba4fb0d7 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/longbaseline_pipeline.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/longbaseline_pipeline.py @@ -22,7 +22,7 @@ import logging from math import ceil -from base_resource_estimator import BaseResourceEstimator +from base_pipeline_estimator import BasePipelineResourceEstimator logger = logging.getLogger(__name__) @@ -31,16 +31,18 @@ PIPELINE = "Observation.ObservationControl.PythonControl." #Observation.DataProducts.Output_Correlated.storageClusterName= -class LongBaselinePipelineResourceEstimator(BaseResourceEstimator): +class LongBaselinePipelineResourceEstimator(BasePipelineResourceEstimator): """ ResourceEstimator for Long Baseline Pipelines """ def __init__(self): logger.info("init LongBaselinePipelineResourceEstimator") - BaseResourceEstimator.__init__(self, name='longbaseline_pipeline') + BasePipelineResourceEstimator.__init__(self, name='pipeline') #FIXME name='longbaseline_pipeline' self.required_keys = ('Observation.startTime', 'Observation.stopTime', DATAPRODUCTS + 'Input_Correlated.enabled', + DATAPRODUCTS + 'Input_Correlated.identifications', DATAPRODUCTS + 'Output_Correlated.enabled', + DATAPRODUCTS + 'Output_Correlated.identifications', PIPELINE + 'LongBaseline.subbandgroups_per_ms', PIPELINE + 'LongBaseline.subbands_per_subbandgroup') @@ -60,7 +62,10 @@ class LongBaselinePipelineResourceEstimator(BaseResourceEstimator): """ logger.debug("start estimate '{}'".format(self.name)) logger.info('parset: %s ' % parset) - result = {'errors': []} + result = {'errors': [], 'storage': {'total_size': 0}, 'bandwidth': {'total_size': 0}} + input_files = self._filterInputs(input_files, parset.getStringVector(DATAPRODUCTS + 'Input_Correlated.identifications')) + result['storage']['input_files'] = input_files + duration = self._getDuration(parset.getString('Observation.startTime'), parset.getString('Observation.stopTime')) subbandgroups_per_ms = parset.getInt(PIPELINE + 'LongBaseline.subbandgroups_per_ms', 0) #TODO, should these have defaults? subbands_per_subbandgroup = parset.getInt(PIPELINE + 'LongBaseline.subbands_per_subbandgroup', 0) @@ -76,21 +81,28 @@ class LongBaselinePipelineResourceEstimator(BaseResourceEstimator): if not subbandgroups_per_ms or not subbands_per_subbandgroup: logger.warning('subbandgroups_per_ms or subbands_per_subbandgroup are not valid') result['errors'].append('Missing UV Dataproducts in input_files') - if nr_input_files % (subbands_per_subband_group * subband_groups_per_ms) > 0: + if nr_input_files % (subbands_per_subbandgroup * subbandgroups_per_ms) > 0: logger.warning('subbandgroups_per_ms and subbands_per_subbandgroup not a multiple of number of inputs') result['errors'].append('subbandgroups_per_ms and subbands_per_subbandgroup not a multiple of number of inputs') if result['errors']: return result logger.debug("calculate correlated data size") - result['output_files'] = {} + result['storage']['output_files'] = {} nr_output_files = nr_input_files / (subbands_per_subbandgroup * subbandgroups_per_ms) - result['output_files']['uv'] = {'nr_of_uv_files': nr_output_files, 'uv_file_size': 1000} # 1 kB was hardcoded in the Scheduler - logger.debug("correlated_uv: {} files {} bytes each".format(result['output_files']['uv']['nr_of_uv_files'], result['output_files']['uv']['uv_file_size'])) + result['storage']['output_files']['uv'] = {'nr_of_uv_files': nr_output_files, + 'uv_file_size': 1000, # 1 kB was hardcoded in the Scheduler + 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_Correlated.identifications'), + 'start_sbg_nr': input_files['uv']['start_sb_nr'] / (subbands_per_subbandgroup * subbandgroups_per_ms)} + logger.debug("correlated_uv: {} files {} bytes each".format(result['storage']['output_files']['uv']['nr_of_uv_files'], result['storage']['output_files']['uv']['uv_file_size'])) # count total data size - total_data_size = result['output_files']['uv']['nr_of_uv_files'] * result['output_files']['uv']['uv_file_size'] # bytes + total_data_size = result['storage']['output_files']['uv']['nr_of_uv_files'] * result['storage']['output_files']['uv']['uv_file_size'] # bytes total_bandwidth = int(ceil((total_data_size * 8) / duration)) # bits/second - result['storage'] = {'total_size': total_data_size} - result['bandwidth'] = {'total_size': total_bandwidth} + if total_data_size: + result['storage']['total_size'] = total_data_size + result['bandwidth']['total_size'] = total_bandwidth + else: + result['errors'].append('Total data size is zero!') + logger.warning('ERROR: A datasize of zero was calculated!') return result diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/observation.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/observation.py index e3e8e1622c7f1e28172caad8d2ceb804b77af5ff..a893e1bac20a9e8610c8536ee002c5541a21bfbc 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/observation.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/observation.py @@ -26,6 +26,7 @@ from base_resource_estimator import BaseResourceEstimator logger = logging.getLogger(__name__) +DATAPRODUCTS = "Observation.DataProducts." COBALT = "Observation.ObservationControl.OnlineControl.Cobalt." class ObservationResourceEstimator(BaseResourceEstimator): @@ -44,9 +45,13 @@ class ObservationResourceEstimator(BaseResourceEstimator): COBALT + 'BeamFormer.CoherentStokes.timeIntegrationFactor', COBALT + 'BeamFormer.IncoherentStokes.timeIntegrationFactor', 'Observation.VirtualInstrument.stationList', - 'Observation.DataProducts.Output_CoherentStokes.enabled', + DATAPRODUCTS + 'Output_Correlated.enabled', + DATAPRODUCTS + 'Output_Correlated.identifications', + DATAPRODUCTS + 'Output_CoherentStokes.enabled', + DATAPRODUCTS + 'Output_CoherentStokes.identifications', COBALT + 'BeamFormer.CoherentStokes.which', - 'Observation.DataProducts.Output_IncoherentStokes.enabled', + DATAPRODUCTS + 'Output_IncoherentStokes.enabled', + DATAPRODUCTS + 'Output_IncoherentStokes.identifications', COBALT + 'BeamFormer.IncoherentStokes.which' ) @@ -68,7 +73,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): logger.info('parset: %s ' % parset) duration = self._getDuration(parset.getString('Observation.startTime'), parset.getString('Observation.stopTime')) - result = {} + result = {'errors': [], 'storage': {'total_size': 0}, 'bandwidth': {'total_size': 0}} output_files = {} correlated_size, correlated_bandwidth, output_files_uv, correlated_saps = self.correlated(parset, duration) coherentstokes_size, coherentstokes_bandwidth, output_files_cs, coherentstokes_saps = self.coherentstokes(parset, duration) @@ -90,16 +95,33 @@ class ObservationResourceEstimator(BaseResourceEstimator): sap['properties'].update(coherentstokes_saps[sap_nr]) if sap_nr in incoherentstokes_saps: sap['properties'].update(incoherentstokes_saps[sap_nr]) + if 'nr_of_tabs' in sap['properties']: # These are coherent TABs + sap['properties']['nr_of_tabs'] = sap['properties']['nr_of_tabs'] + 1 + else: + sap['properties']['nr_of_tabs'] = 1 # Only an incoherent TAB for this SAP output_files['saps'].append(sap) + total_data_size = correlated_size + coherentstokes_size + incoherentstokes_size - result['storage'] = {'total_size': total_data_size, 'output_files': output_files} - result['bandwidth'] = {'total_size': correlated_bandwidth + coherentstokes_bandwidth + incoherentstokes_bandwidth} + if total_data_size and output_files: + result['storage'] = {'total_size': total_data_size, 'output_files': output_files} + result['bandwidth'] = {'total_size': correlated_bandwidth + coherentstokes_bandwidth + incoherentstokes_bandwidth} + else: + if not total_data_size: + result['errors'].append('Total data size is zero!') + logger.warning('ERROR: A datasize of zero was calculated!') + if not output_files: + result['errors'].append('No output files!') + logger.warning('ERROR: No output files were calculated!') return result def correlated(self, parset, duration): """ Estimate number of files, file size and bandwidth needed for correlated data """ + if not parset.getBool('Observation.DataProducts.Output_Correlated.enabled'): + logger.info("No correlated data") + return (0,0, {}, {}) + logger.info("calculating correlated datasize") size_of_header = 512 #TODO More magic numbers (probably from Alwin). ScS needs to check these. They look ok though. size_of_overhead = 600000 @@ -122,11 +144,11 @@ class ObservationResourceEstimator(BaseResourceEstimator): for sap_nr in xrange(parset.getInt('Observation.nrBeams')): subbandList = parset.getStringVector('Observation.Beam[%d].subbandList' % sap_nr) nr_files = len(subbandList) + sap_files[sap_nr] = {'nr_of_uv_files': nr_files, 'start_sb_nr': total_files} total_files += nr_files - sap_files[sap_nr]= {'nr_of_uv_files': nr_files} file_size = int((data_size + n_sample_size + size_of_header) * integrated_seconds + size_of_overhead) - output_files = {'nr_of_uv_files': total_files, 'uv_file_size': file_size} + output_files = {'nr_of_uv_files': total_files, 'uv_file_size': file_size, 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_Correlated.identifications')} logger.info("correlated_uv: {} files {} bytes each".format(total_files, file_size)) total_data_size = int(ceil(file_size * total_files)) # bytes @@ -137,6 +159,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): """ Estimate number of files, file size and bandwidth needed for coherent stokes """ if not parset.getBool('Observation.DataProducts.Output_CoherentStokes.enabled'): + logger.info("No coherent stokes data") return (0,0, {}, {}) logger.info("calculate coherentstokes datasize") @@ -168,7 +191,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): logger.info("checking TAB {}".format(tab_nr)) if parset.getBool("Observation.Beam[%d].TiedArrayBeam[%d].coherent" % (sap_nr, tab_nr)): logger.info("adding coherentstokes size") - nr_stokes = nr_coherent #TODO what does min mean here? + nr_stokes = nr_coherent # TODO, there used to be a function with min() here? total_nr_tabs += 1 total_nr_stokes += nr_stokes nr_files += int(nr_stokes * ceil(nr_subbands / float(subbands_per_file))) @@ -189,14 +212,15 @@ class ObservationResourceEstimator(BaseResourceEstimator): nr_stokes = nr_tabs * nr_coherent total_nr_stokes += nr_stokes nr_files += int(nr_stokes * ceil(nr_subbands / float(subbands_per_file))) - - sap_files[sap_nr]= {'nr_of_cs_files': nr_files, 'nr_of_tabs': total_nr_tabs} - total_files += nr_files + + if nr_files: + sap_files[sap_nr]= {'nr_of_cs_files': nr_files, 'nr_of_tabs': total_nr_tabs} + total_files += nr_files nr_subbands_per_file = min(subbands_per_file, max_nr_subbands) size_per_file = int(nr_subbands_per_file * size_per_subband) - output_files = {'nr_of_cs_files': total_files, 'nr_of_cs_stokes': nr_coherent, 'cs_file_size': size_per_file} + output_files = {'nr_of_cs_files': total_files, 'nr_of_cs_stokes': nr_coherent, 'cs_file_size': size_per_file, 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_CoherentStokes.identifications')} logger.info("coherentstokes: {} files {} bytes each".format(total_files, size_per_file)) total_data_size = int(ceil(total_nr_stokes * max_nr_subbands * size_per_subband)) @@ -207,6 +231,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): """ Estimate number of files, file size and bandwidth needed for incoherentstokes """ if not parset.getBool('Observation.DataProducts.Output_IncoherentStokes.enabled'): + logger.info("No incoherent stokes data") return (0,0, {}, {}) logger.info("calculate incoherentstokes data size") @@ -217,7 +242,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): channels_per_subband = parset.getInt(COBALT + 'Correlator.nrChannelsPerSubband', 64) #TODO should these have defaults? incoherent_channels_per_subband = parset.getInt(COBALT + 'BeamFormer.IncoherentStokes.nrChannelsPerSubband', 0) - nr_incoherent = 4 if incoherent_type in ('IQUV',) else 1 + nr_incoherent = 4 if incoherent_type in ('IQUV',) else 1 # Should this also include XXYY ? total_nr_stokes = 0 total_files = 0 @@ -230,16 +255,23 @@ class ObservationResourceEstimator(BaseResourceEstimator): nr_subbands = len(subbandList) max_nr_subbands = max(nr_subbands, max_nr_subbands) nr_files = 0 + is_tab_nr = -1 total_nr_tabs = parset.getInt('Observation.Beam[%d].nrTiedArrayBeams' % sap_nr) for tab_nr in xrange(total_nr_tabs): logger.info("checking TAB {}".format(tab_nr)) - if not parset.getBool("Observation.Beam[%d].TiedArrayBeam[%d].coherent" % (sap_nr, tab_nr)): - logger.info("adding incoherentstokes size") - total_nr_stokes += nr_incoherent - nr_files += int(nr_incoherent * ceil(nr_subbands / float(subbands_per_file))) - - sap_files[sap_nr]= {'nr_of_is_files': nr_files, 'nr_of_tabs': total_nr_tabs} - total_files += nr_files + if not parset.getBool("Observation.Beam[%d].TiedArrayBeam[%d].coherent" % (sap_nr, tab_nr)): #not coherent is incoherent + logger.info("Found incoherent stokes TAB: %i" % tab_nr) + if is_tab_nr >= 0: + logger.warning("TAB nr %i can't be incoherent as %i already is!" % (tab_nr, is_tab_nr)) + # TODO We need to generate an error here, or preferably check before we get here + else: + is_tab_nr = tab_nr + total_nr_stokes += nr_incoherent + nr_files += int(nr_incoherent * ceil(nr_subbands / float(subbands_per_file))) + + if nr_files: + sap_files[sap_nr] = {'nr_of_is_files': nr_files, 'is_tab_nr': is_tab_nr} + total_files += nr_files if incoherent_channels_per_subband > 0: channel_integration_factor = channels_per_subband / incoherent_channels_per_subband @@ -251,7 +283,7 @@ class ObservationResourceEstimator(BaseResourceEstimator): size_per_subband = int((samples_per_second * 4) / time_integration_factor / channel_integration_factor * duration) size_per_file = nr_subbands_per_file * size_per_subband - output_files = {'nr_of_is_files': total_files, 'nr_of_is_stokes': nr_incoherent, 'is_file_size': int(size_per_file)} + output_files = {'nr_of_is_files': total_files, 'nr_of_is_stokes': nr_incoherent, 'is_file_size': int(size_per_file), 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_IncoherentStokes.identifications')} logger.info("incoherentstokes: {} files {} bytes each".format(total_files, size_per_file)) total_data_size = int(ceil(total_nr_stokes * max_nr_subbands * size_per_subband)) # bytes diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/pulsar_pipeline.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/pulsar_pipeline.py index c3b80967d1c991760840261ea9dbea8e18e12b33..a36a1d718db189bce5526ed27f6e3808914474b1 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/pulsar_pipeline.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/resource_estimators/pulsar_pipeline.py @@ -22,7 +22,7 @@ import logging from math import ceil -from base_resource_estimator import BaseResourceEstimator +from base_pipeline_estimator import BasePipelineResourceEstimator logger = logging.getLogger(__name__) @@ -31,17 +31,20 @@ PIPELINE = "Observation.ObservationControl.PythonControl." #Observation.DataProducts.Output_Correlated.storageClusterName= -class PulsarPipelineResourceEstimator(BaseResourceEstimator): +class PulsarPipelineResourceEstimator(BasePipelineResourceEstimator): """ ResourceEstimator for Pulsar Pipelines """ def __init__(self): logger.info("init PulsarPipelineResourceEstimator") - BaseResourceEstimator.__init__(self, name='pulsar_pipeline') + BasePipelineResourceEstimator.__init__(self, name='pipeline') #FIXME name='pulsar_pipeline' self.required_keys = ('Observation.startTime', 'Observation.stopTime', DATAPRODUCTS + 'Input_CoherentStokes.enabled', + DATAPRODUCTS + 'Input_CoherentStokes.identifications', DATAPRODUCTS + 'Input_IncoherentStokes.enabled', - DATAPRODUCTS + 'Output_Pulsar.enabled') + DATAPRODUCTS + 'Input_IncoherentStokes.identifications', + DATAPRODUCTS + 'Output_Pulsar.enabled', + DATAPRODUCTS + 'Output_Pulsar.identifications') def _calculate(self, parset, input_files): """ Estimate for Pulsar Pipeline @@ -59,7 +62,11 @@ class PulsarPipelineResourceEstimator(BaseResourceEstimator): """ logger.debug("start estimate '{}'".format(self.name)) logger.info('parset: %s ' % parset) - result = {'errors': []} + result = {'errors': [], 'storage': {'total_size': 0}, 'bandwidth': {'total_size': 0}} + input_files = self._filterInputs(input_files, parset.getStringVector(DATAPRODUCTS + 'Input_CoherentStokes.identifications') + + parset.getStringVector(DATAPRODUCTS + 'Input_IncoherentStokes.identifications')) + result['storage']['input_files'] = input_files + duration = self._getDuration(parset.getString('Observation.startTime'), parset.getString('Observation.stopTime')) if not parset.getBool(DATAPRODUCTS + 'Output_Pulsar.enabled'): @@ -72,7 +79,7 @@ class PulsarPipelineResourceEstimator(BaseResourceEstimator): return result logger.debug("calculate pulp data size") - result['output_files'] = {} + result['storage']['output_files'] = {} nr_output_files = 0 if 'cs' in input_files: nr_input_files = input_files['cs']['nr_of_cs_files'] @@ -85,12 +92,17 @@ class PulsarPipelineResourceEstimator(BaseResourceEstimator): nr_input_files = input_files['is']['nr_of_is_files'] nr_output_files += nr_input_files - result['output_files']['pulp'] = {'nr_of_pulp_files': nr_output_files, 'pulp_file_size': 1000} # 1 kB was hardcoded in the Scheduler - logger.debug("correlated_uv: {} files {} bytes each".format(result['output_files']['pulp']['nr_of_pulp_files'], result['output_files']['pulp']['pulp_file_size'])) + result['storage']['output_files']['pulp'] = {'nr_of_pulp_files': nr_output_files, 'pulp_file_size': 1000, 'identifications': parset.getStringVector(DATAPRODUCTS + 'Output_Pulsar.identifications')} # 1 kB was hardcoded in the Scheduler + logger.info(result) + logger.info("pulsar_pipeline pulp: {} files {} bytes each".format(result['storage']['output_files']['pulp']['nr_of_pulp_files'], result['storage']['output_files']['pulp']['pulp_file_size'])) # count total data size - total_data_size = result['output_files']['pulp']['nr_of_pulp_files'] * result['output_files']['pulp']['pulp_file_size'] # bytes + total_data_size = result['storage']['output_files']['pulp']['nr_of_pulp_files'] * result['storage']['output_files']['pulp']['pulp_file_size'] # bytes total_bandwidth = int(ceil((total_data_size * 8) / duration)) # bits/second - result['storage'] = {'total_size': total_data_size} - result['bandwidth'] = {'total_size': total_bandwidth} + if total_data_size: + result['storage']['total_size'] = total_data_size + result['bandwidth']['total_size'] = total_bandwidth + else: + result['errors'].append('Total data size is zero!') + logger.warning('ERROR: A datasize of zero was calculated!') return result diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/service.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/service.py index a6b9d4cbcccc90b84e57acee8974cbbd8a0bad62..a0f720bb3484f288e893050807e46f3b2f7eb651 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/service.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/service.py @@ -6,6 +6,7 @@ Simple Service listening ''' import logging +import pprint from lofar.messaging import Service from lofar.messaging.Service import MessageHandlerInterface @@ -27,44 +28,71 @@ class ResourceEstimatorHandler(MessageHandlerInterface): specification_tree = content["specification_tree"] return self._get_estimated_resources(specification_tree) ##TODO also handle MoM tasks in RA 1.2 + ##FIXME dirty hack + def add_id(self, estimate, otdb_id): + if 'storage' in estimate.values()[0]: + if 'output_files' in estimate.values()[0]['storage']: #We only need to do output files, it will be someone else's input + for data_type in estimate.values()[0]['storage']['output_files'].keys(): + if data_type != 'saps': + estimate.values()[0]['storage']['output_files'][data_type][data_type + '_otdb_id'] = otdb_id + logger.info('added %s to %s' % (otdb_id, str(estimate.values()[0]['storage']['output_files'][data_type]))) + return estimate + + #TODO use something else than .values()[0]['storage'] ?? def get_subtree_estimate(self, specification_tree): otdb_id = specification_tree['otdb_id'] parset = specification_tree['specification'] if specification_tree['task_type'] == 'observation': - return {str(otdb_id): self.observation.verify_and_estimate(parset)} + return {str(otdb_id): self.add_id(self.observation.verify_and_estimate(parset), otdb_id)} elif specification_tree['task_type'] == 'pipeline': + if not 'predecessors' in specification_tree or not specification_tree['predecessors']: + logger.warning("Could not estimate %s because the pipeline has no predecessors" % (otdb_id)) + return {str(otdb_id): {'pipeline': {'errors': ["Could not estimate %s because the pipeline has no predecessors" % (otdb_id)]}}} + branch_estimates = {} for branch in specification_tree['predecessors']: - branch_estimates.update(get_subtree_estimate(branch)) - + subtree_estimate = self.get_subtree_estimate(branch) + if subtree_estimate[str(branch['otdb_id'])][branch['task_type']]['errors']: + logger.warning("Could not estimate %s because predecessor %s has errors" % (otdb_id, branch)) + return {str(otdb_id): {'pipeline': {'errors': ["Could not estimate %s because predecessor %s has errors" % (otdb_id, branch)]}}} + branch_estimates.update(subtree_estimate) + logger.info(("Branch estimates for %s\n" % otdb_id) + pprint.pformat(branch_estimates)) + if specification_tree['task_subtype'] in ['averaging pipeline', 'calibration pipeline']: - for id, estimate in branch_estimates: - if not 'im' in estimate['output_files'] and 'uv' in estimate['output_files']: # Not a calibrator pipeline - logger.info('found %d as the target of pipeline %d' % (id, otdb_id)) - input_files = estimate['output_files'] # Need sap here as well - return {str(otdb_id): self.calibration_pipeline.verify_and_estimate(parset, input_files)} - + input_files = {} + for id, estimate in branch_estimates.iteritems(): + logger.info('Looking at predecessor %s' % id) + predecessor_output = estimate.values()[0]['storage']['output_files'] + if not 'im' in predecessor_output and 'uv' in predecessor_output: # Not a calibrator pipeline + logger.info('found %s as the target of pipeline %s' % (id, otdb_id)) + input_files['uv'] = predecessor_output['uv'] + if 'saps' in predecessor_output: + input_files['saps'] = predecessor_output['saps'] + elif 'im' in predecessor_output: + logger.info('found %s as the calibrator of pipeline %s' % (id, otdb_id)) + input_files['im'] = predecessor_output['im'] + return {str(otdb_id): self.add_id(self.calibration_pipeline.verify_and_estimate(parset, input_files), otdb_id)} + + if len(branch_estimates) > 1: + logger.warning('Pipeline %d should not have multiple predecessors: %s' % (otdb_id, branch_estimates.keys())) + return {str(otdb_id): {'pipeline': {'errors': ['Pipeline %d should not have multiple predecessors: %s' % (otdb_id, branch_estimates.keys())]}}} + predecessor_output = branch_estimates.values()[0].values()[0]['storage']['output_files'] + if specification_tree['task_subtype'] in ['imaging pipeline', 'imaging pipeline msss']: - if len(branch_estimates) > 1: - logger.error('Imaging pipeline %d should not have multiple predecessors: %s' % (otdb_id, branch_estimates.keys() ) ) - input_files = branch_estimates.items()[0][1]['ouput_files'] - return {str(otdb_id): self.calibration_pipeline.verify_and_estimate(parset, input_files)} + input_files = predecessor_output + return {str(otdb_id): self.add_id(self.imaging_pipeline.verify_and_estimate(parset, input_files), otdb_id)} if specification_tree['task_subtype'] in ['long baseline pipeline']: - if len(branch_estimates) > 1: - logger.error('Long baseline pipeline %d should not have multiple predecessors: %s' % (otdb_id, branch_estimates.keys() ) ) - input_files = branch_estimates.items()[0][1]['ouput_files'] - return {str(otdb_id): self.longbaseline_pipeline.verify_and_estimate(parset, input_files)} + input_files = predecessor_output + return {str(otdb_id): self.add_id(self.longbaseline_pipeline.verify_and_estimate(parset, input_files), otdb_id)} if specification_tree['task_subtype'] in ['pulsar pipeline']: - if len(branch_estimates) > 1: - logger.error('Pulsar pipeline %d should not have multiple predecessors: %s' % (otdb_id, branch_estimates.keys() ) ) - input_files = branch_estimates.items()[0][1]['ouput_files'] - return {str(otdb_id): self.pulsar_pipeline.verify_and_estimate(parset, input_files)} + input_files = predecessor_output + return {str(otdb_id): self.add_id(self.pulsar_pipeline.verify_and_estimate(parset, input_files), otdb_id)} else: # reservation, maintenance, system tasks? - logger.info("It's not a pipeline or observation: %s" % otdb_id) - return {str(otdb_id): {}} + logger.warning("ID %s is not a pipeline or observation." % otdb_id) + return {str(otdb_id): {specification_tree['task_type']: {'errors': ["ID %s is not a pipeline or observation." % otdb_id]}}} def _get_estimated_resources(self, specification_tree): """ Input is like: @@ -96,6 +124,10 @@ def createService(busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, brok verbose=True) def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + from optparse import OptionParser from lofar.messaging import setQpidLogLevel from lofar.common.util import waitForInterrupt diff --git a/SAS/ResourceAssignment/ResourceAssignmentEstimator/test/t_resource_estimator.py b/SAS/ResourceAssignment/ResourceAssignmentEstimator/test/t_resource_estimator.py index 0ffcb51efee0edb29509a109aa723b978855e022..27a1ad8871f4f89ee05febadb27d0f0e7039048e 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentEstimator/test/t_resource_estimator.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEstimator/test/t_resource_estimator.py @@ -96,7 +96,7 @@ try: # create and run the service with createService(busname=busname): # and run all tests - unittest.main(verbosity=2) + unittest.main() finally: # cleanup test bus and exit diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/radbservice.ini b/SAS/ResourceAssignment/ResourceAssignmentService/radbservice.ini index 676186bcb9dd437febb3ef2213abffbea2889d7b..95b1a0b7521c40cc4ffd1afad0f4c211dd9cc9a4 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentService/radbservice.ini +++ b/SAS/ResourceAssignment/ResourceAssignmentService/radbservice.ini @@ -1,5 +1,5 @@ [program:radbservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;radbservice --log-queries' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec radbservice --log-queries' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py b/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py index cff26544ec6771cefe10b22fc7aab15506504107..e7720e0acfa5cda84666a5c7fccd9ad6ac976f5e 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py +++ b/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py @@ -21,8 +21,9 @@ class RARPCException(Exception): class RARPC(RPCWrapper): def __init__(self, busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, - broker=None): - super(RARPC, self).__init__(busname, servicename, broker) + broker=None, + timeout=120): + super(RARPC, self).__init__(busname, servicename, broker, timeout=timeout) def getResourceClaimStatuses(self): return self.rpc('GetResourceClaimStatuses') @@ -46,8 +47,9 @@ class RARPC(RPCWrapper): extended=extended, include_properties=include_properties) - logger.info("found %s claims" % len(claims)) - + logger.debug("found %s claims for claim_ids=%s, lower_bound=%s, upper_bound=%s, task_ids=%s, status=%s, resource_type=%s", + len(claims), claim_ids, lower_bound, upper_bound, task_ids, status, resource_type) + for claim in claims: claim['starttime'] = claim['starttime'].datetime() claim['endtime'] = claim['endtime'].datetime() @@ -145,9 +147,9 @@ class RARPC(RPCWrapper): available_capacity=available_capacity, total_capacity=total_capacity) - def getTask(self, id=None, mom_id=None, otdb_id=None): - '''get a task for either the given (task)id, or for the given mom_id, or for the given otdb_id''' - task = self.rpc('GetTask', id=id, mom_id=mom_id, otdb_id=otdb_id) + def getTask(self, id=None, mom_id=None, otdb_id=None, specification_id=None): + '''get a task for either the given (task)id, or for the given mom_id, or for the given otdb_id, or for the given specification_id''' + task = self.rpc('GetTask', id=id, mom_id=mom_id, otdb_id=otdb_id, specification_id=specification_id) if task: task['starttime'] = task['starttime'].datetime() task['endtime'] = task['endtime'].datetime() @@ -177,13 +179,35 @@ class RARPC(RPCWrapper): otdb_id=otdb_id, status=status) - def getTasks(self, lower_bound=None, upper_bound=None): - tasks = self.rpc('GetTasks', lower_bound=lower_bound, upper_bound=upper_bound) + def getTasksTimeWindow(self, task_ids=None, mom_ids=None, otdb_ids=None): + result = self.rpc('GetTasksTimeWindow', task_ids=task_ids, mom_ids=mom_ids, otdb_ids=otdb_ids) + result['min_starttime'] = result['min_starttime'].datetime() + result['max_endtime'] = result['max_endtime'].datetime() + return result + + def getTasks(self, lower_bound=None, upper_bound=None, task_ids=None, task_status=None, task_type=None, mom_ids=None, otdb_ids=None, cluster=None): + '''getTasks let's you query tasks from the radb with many optional filters. + :param lower_bound: datetime specifies the lower_bound of a time window above which to select tasks + :param upper_bound: datetime specifies the upper_bound of a time window below which to select tasks + :param task_ids: int/list/tuple specifies one or more task_ids to select + :param task_status: int/string/list specifies one or more task_statuses to select in either task_status_id or task_status_name form + :param task_type: int/string/list specifies one or more task_types to select in either task_type_id or task_type_name form + :param mom_ids: int/list/tuple specifies one or more mom_ids to select + :param otdb_ids: int/list/tuple specifies one or more otdb_ids to select + :param cluster: string specifies the cluster to select + ''' + tasks = self.rpc('GetTasks', lower_bound=lower_bound, upper_bound=upper_bound, task_ids=task_ids, task_status=task_status, task_type=task_type, mom_ids=mom_ids, otdb_ids=otdb_ids, cluster=cluster) for task in tasks: task['starttime'] = task['starttime'].datetime() task['endtime'] = task['endtime'].datetime() return tasks + def getTaskPredecessorIds(self, id=None): + return self.rpc('GetTaskPredecessorIds', id=id) + + def getTaskSuccessorIds(self, **kwargs): + return self.rpc('GetTaskSuccessorIds', id=id) + def insertTaskPredecessor(self, task_id, predecessor_id): return self.rpc('InsertTaskPredecessor', task_id=task_id, predecessor_id=predecessor_id) @@ -203,7 +227,7 @@ class RARPC(RPCWrapper): specification['endtime'] = specification['endtime'].datetime() return specification - def insertSpecificationAndTask(self, mom_id, otdb_id, task_status, task_type, starttime, endtime, content): + def insertSpecificationAndTask(self, mom_id, otdb_id, task_status, task_type, starttime, endtime, content, cluster): return self.rpc('InsertSpecificationAndTask', mom_id=mom_id, otdb_id=otdb_id, @@ -211,23 +235,26 @@ class RARPC(RPCWrapper): task_type=task_type, starttime=starttime, endtime=endtime, - content=content) + content=content, + cluster=cluster) - def insertSpecification(self, starttime, endtime, content): + def insertSpecification(self, starttime, endtime, content, cluster): return self.rpc('InsertSpecification', starttime=starttime, endtime=endtime, - content=content) + content=content, + cluster=cluster) def deleteSpecification(self, id): return self.rpc('DeleteSpecification', id=id) - def updateSpecification(self, id, starttime=None, endtime=None, content=None): + def updateSpecification(self, id, starttime=None, endtime=None, content=None, cluster=None): return self.rpc('UpdateSpecification', id=id, starttime=starttime, endtime=endtime, - content=content) + content=content, + cluster=cluster) def getSpecifications(self): specifications = self.rpc('GetSpecifications') diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/service.py b/SAS/ResourceAssignment/ResourceAssignmentService/service.py index c48ae9867ece5fed7ab0671eb47d4a3502b27670..6c9138a8a11b3f2b0667dcbf3fb6cdd1263539d5 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentService/service.py +++ b/SAS/ResourceAssignment/ResourceAssignmentService/service.py @@ -56,14 +56,17 @@ class RADBHandler(MessageHandlerInterface): 'GetResourceTypes': self._getResourceTypes, 'GetResources': self._getResources, 'UpdateResourceAvailability': self._updateResourceAvailability, + 'GetTasksTimeWindow': self._getTasksTimeWindow, 'GetTasks': self._getTasks, 'GetTask': self._getTask, 'InsertTask': self._insertTask, 'DeleteTask': self._deleteTask, 'UpdateTask': self._updateTask, 'UpdateTaskStatusForOtdbId': self._updateTaskStatusForOtdbId, + 'GetTaskPredecessorIds': self._getTaskPredecessorIds, + 'GetTaskSuccessorIds': self._getTaskSuccessorIds, 'InsertTaskPredecessor': self._insertTaskPredecessor, - 'insertTaskPredecessors': self._insertTaskPredecessors, + 'InsertTaskPredecessors': self._insertTaskPredecessors, 'GetTaskStatuses': self._getTaskStatuses, 'GetTaskTypes': self._getTaskTypes, 'GetSpecifications': self._getSpecifications, @@ -151,12 +154,11 @@ class RADBHandler(MessageHandlerInterface): updated = self.radb.updateResourceClaim(id, resource_id=kwargs.get('resource_id'), task_id=kwargs.get('task_id'), - starttime=kwargs['starttime'].datetime() if 'starttime' in kwargs else None, - endtime=kwargs['endtime'].datetime() if 'endtime' in kwargs else None, + starttime=kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, + endtime=kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, status=kwargs.get('status_id', kwargs.get('status')), session_id=kwargs.get('session_id'), claim_size=kwargs.get('claim_size'), - nr_of_parts=kwargs.get('nr_of_parts'), username=kwargs.get('username'), user_id=kwargs.get('user_id')) return {'id': id, 'updated': updated} @@ -164,17 +166,10 @@ class RADBHandler(MessageHandlerInterface): def _updateTaskAndResourceClaims(self, **kwargs): logger.info('UpdateTaskAndResourceClaims: %s' % dict({k:v for k,v in kwargs.items() if v != None})) task_id = kwargs['task_id'] - starttime = kwargs.get('starttime') - if starttime: - starttime = starttime.datetime(); - - endtime = kwargs.get('endtime') - if endtime: - endtime = endtime.datetime(); updated = self.radb.updateTaskAndResourceClaims(task_id, - starttime=starttime, - endtime=endtime, + starttime=kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, + endtime=kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, task_status=kwargs.get('task_status_id', kwargs.get('task_status')), claim_status=kwargs.get('claim_status_id', kwargs.get('claim_status')), session_id=kwargs.get('session_id'), @@ -217,14 +212,26 @@ class RADBHandler(MessageHandlerInterface): available_capacity=kwargs.get('available_capacity'), total_capacity=kwargs.get('total_capacity')) + def _getTasksTimeWindow(self, **kwargs): + logger.info('GetTasksTimeWindow: %s' % dict({k:v for k,v in kwargs.items() if v != None})) + return self.radb.getTasksTimeWindow(task_ids=kwargs.get('task_ids'), + mom_ids=kwargs.get('mom_ids'), + otdb_ids=kwargs.get('otdb_ids')) + def _getTasks(self, **kwargs): logger.info('GetTasks: %s' % dict({k:v for k,v in kwargs.items() if v != None})) return self.radb.getTasks(lower_bound=kwargs.get('lower_bound').datetime() if kwargs.get('lower_bound') else None, - upper_bound=kwargs.get('upper_bound').datetime() if kwargs.get('upper_bound') else None) + upper_bound=kwargs.get('upper_bound').datetime() if kwargs.get('upper_bound') else None, + task_ids=kwargs.get('task_ids'), + task_status=kwargs.get('task_status'), + task_type=kwargs.get('task_type'), + mom_ids=kwargs.get('mom_ids'), + otdb_ids=kwargs.get('otdb_ids'), + cluster=kwargs.get('cluster')) def _getTask(self, **kwargs): logger.info('GetTask: %s' % dict({k:v for k,v in kwargs.items() if v != None})) - task = self.radb.getTask(id=kwargs.get('id'), mom_id=kwargs.get('mom_id'), otdb_id=kwargs.get('otdb_id')) + task = self.radb.getTask(id=kwargs.get('id'), mom_id=kwargs.get('mom_id'), otdb_id=kwargs.get('otdb_id'), specification_id=kwargs.get('specification_id')) return task def _insertTask(self, **kwargs): @@ -260,6 +267,14 @@ class RADBHandler(MessageHandlerInterface): specification_id=kwargs.get('specification_id')) return {'id': id, 'updated': updated} + def _getTaskPredecessorIds(self, **kwargs): + logger.info('GetTaskPredecessorIds: %s' % dict({k:v for k,v in kwargs.items() if v != None})) + return convertIntKeysToString(self.radb.getTaskPredecessorIds(kwargs.get('id'))) + + def _getTaskSuccessorIds(self, **kwargs): + logger.info('GetTaskSuccessorIds: %s' % dict({k:v for k,v in kwargs.items() if v != None})) + return convertIntKeysToString(self.radb.getTaskSuccessorIds(kwargs.get('id'))) + def _insertTaskPredecessor(self, **kwargs): id = self.radb.insertTaskPredecessor(kwargs['task_id'], kwargs['predecessor_id']) @@ -284,15 +299,17 @@ class RADBHandler(MessageHandlerInterface): kwargs['otdb_id'], kwargs['task_status'], kwargs['task_type'], - kwargs['starttime'].datetime(), - kwargs['endtime'].datetime(), - kwargs['content']) + kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, + kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, + kwargs['content'], + kwargs['cluster']) def _insertSpecification(self, **kwargs): logger.info('InsertSpecification: %s' % dict({k:v for k,v in kwargs.items() if v != None})) - specification_id = self.radb.insertSpecification(kwargs['starttime'].datetime(), - kwargs['endtime'].datetime(), - kwargs['content']) + specification_id = self.radb.insertSpecification(kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, + kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, + kwargs['content'], + kwargs['cluster']) return {'id':specification_id} def _deleteSpecification(self, **kwargs): @@ -307,7 +324,8 @@ class RADBHandler(MessageHandlerInterface): updated = self.radb.updateSpecification(id, starttime=kwargs['starttime'].datetime() if 'starttime' in kwargs else None, endtime=kwargs['endtime'].datetime() if 'endtime' in kwargs else None, - content=kwargs.get('content')) + content=kwargs.get('content'), + cluster=kwargs.get('cluster')) return {'id': id, 'updated': updated} def _getUnits(self): @@ -324,6 +342,10 @@ def createService(busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, brok verbose=verbose) def main(): + # make sure we run in UTC timezone + import os + os.environ['TZ'] = 'UTC' + # Check the invocation arguments parser = OptionParser("%prog [options]", description='runs the resourceassignment database service') diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.py b/SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.py index 6557d8d76ffdae3f5962be15d2a4830fbfaa3891..a5d727a0a9629a188eee580679b11b7f0b8dc9c7 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.py +++ b/SAS/ResourceAssignment/ResourceAssignmentService/test/test_ra_service_and_rpc.py @@ -94,7 +94,7 @@ try: with createService(busname=busname): # and run all tests - unittest.main(verbosity=2) + unittest.main() except ConnectError as ce: logger.error(ce) diff --git a/SAS/ResourceAssignment/Services/CMakeLists.txt b/SAS/ResourceAssignment/Services/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fd8e52e033e4de0cb4010ff14ff02e22c3deb517 --- /dev/null +++ b/SAS/ResourceAssignment/Services/CMakeLists.txt @@ -0,0 +1,8 @@ +# $Id$ + +lofar_package(ResourceAssignmentServices 1.0 DEPENDS PyMessaging PyCommon pyparameterset OTDB_Services) + +include(PythonInstall) + +add_subdirectory(src) +add_subdirectory(test) diff --git a/SAS/ResourceAssignment/Services/src/CMakeLists.txt b/SAS/ResourceAssignment/Services/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..69a3891eb85e3b053a050947496e081cf36e16af --- /dev/null +++ b/SAS/ResourceAssignment/Services/src/CMakeLists.txt @@ -0,0 +1,16 @@ +# $Id$ + +lofar_add_bin_scripts( + rataskspecifiedservice +) + +python_install( + RATaskSpecified.py + RABusListener.py + DESTINATION lofar/sas/resourceassignment +) + +# supervisord config files +install(FILES + rataskspecifiedservice.ini + DESTINATION etc/supervisord.d) diff --git a/SAS/ResourceAssignment/Services/src/RABusListener.py b/SAS/ResourceAssignment/Services/src/RABusListener.py new file mode 100644 index 0000000000000000000000000000000000000000..6d5f5a7712577503bedd09cda5845123905031cb --- /dev/null +++ b/SAS/ResourceAssignment/Services/src/RABusListener.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# RABusListener.py: RABusListener listens on the lofar ra message bus and calls (empty) on<SomeMessage> methods when such a message is received. +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id$ + +""" +RABusListener listens on the lofar otdb message bus and calls (empty) on<SomeMessage> methods when such a message is received. +Typical usage is to derive your own subclass from RABusListener and implement the specific on<SomeMessage> methods that you are interested in. +""" + +from lofar.messaging.messagebus import AbstractBusListener + +import qpid.messaging +import logging +from datetime import datetime + +logger = logging.getLogger(__name__) + + +class RATaskSpecifiedBusListener(AbstractBusListener): + def __init__(self, busname='lofar.ra.notification', subject='OTDB.TaskSpecified', broker=None, **kwargs): + """ + RATaskSpecifiedBusListener listens on the lofar ra message bus and calls (empty) on<SomeMessage> methods when such a message is received. + Typical usage is to derive your own subclass from RATaskSpecifiedBusListener and implement the specific on<SomeMessage> methods that you are interested in. + :param address: valid Qpid address (default: lofar.otdb.status) + :param broker: valid Qpid broker host (default: None, which means localhost) + additional parameters in kwargs: + options= <dict> Dictionary of options passed to QPID + exclusive= <bool> Create an exclusive binding so no other services can consume duplicate messages (default: False) + numthreads= <int> Number of parallel threads processing messages (default: 1) + verbose= <bool> Output extra logging over stdout (default: False) + """ + address = "%s/%s" % (busname, subject) + super(RATaskSpecifiedBusListener, self).__init__(address, broker, **kwargs) + + def _handleMessage(self, msg): + logger.debug("RABusListener.handleMessage: %s" %str(msg)) + + sasId = msg.content['sasID'] + modificationTime = msg.content['time_of_change'].datetime() + resource_indicators = msg.content['resource_indicators'] + + self.onTaskSpecified(sasId, modificationTime, resource_indicators) + + def onTaskSpecified(self, sasId, modificationTime, resourceIndicators): + pass + +__all__ = ["RATaskSpecifiedBusListener"] diff --git a/SAS/ResourceAssignment/Services/src/RATaskSpecified.py b/SAS/ResourceAssignment/Services/src/RATaskSpecified.py new file mode 100755 index 0000000000000000000000000000000000000000..e05047d467f60f5276525967282e9b7e47d730cb --- /dev/null +++ b/SAS/ResourceAssignment/Services/src/RATaskSpecified.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python +#coding: iso-8859-15 +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id$ +""" +Daemon that listens to OTDB status changes to PRESCHEDULED and SCHEDULED, requests +the parset of such jobs (+ their predecessors), and posts them on the bus. +""" + +from lofar.messaging import FromBus, ToBus, RPC, EventMessage +from lofar.parameterset import PyParameterValue +from lofar.sas.otdb.OTDBBusListener import OTDBBusListener +from lofar.common.util import waitForInterrupt + +import logging +logger = logging.getLogger(__name__) + +""" Prefix that is common to all parset keys, depending on the exact source. """ +PARSET_PREFIX="ObsSW." + +def predecessors( parset ): + """ Extract the list of predecessor obs IDs from the given parset. """ + + key = PARSET_PREFIX + "Observation.Scheduler.predecessors" + strlist = PyParameterValue(str(parset[key]), True).getStringVector() + + # Key contains "Lxxxxx" values, we want to have "xxxxx" only + result = [int(filter(str.isdigit,x)) for x in strlist] + + return result + +def resourceIndicatorsFromParset( parset ): + """ Extract the parset keys that are required for resource assignment. """ + + subset = {} + + def get(key, default=None): + """ Return the value of parset key `key', or `default' if the key + is not defined. """ + return parset.get(PARSET_PREFIX + key, default) + + def add(key, conversion=lambda x: x): + """ Add the given key to our subset selection, using an optional + conversion. """ + value = get(key) + if value is not None: + subset[key] = conversion(value) + + """ Some conversion functions for common parameter-value types.""" + def strvector(value): + return PyParameterValue(value, True).getStringVector() + + def intvector(value): + return PyParameterValue(value, True).getIntVector() + + def bool(value): + return PyParameterValue(value, True).getBool() + + # ===================================== + # Parset meta info + # ===================================== + subset["Version.number"] = parset.get("Version.number") + + # ===================================== + # Observation settings + # ===================================== + add("Observation.sampleClock") + add("Observation.nrBitsPerSample") + add("Observation.antennaSet") + add("Observation.VirtualInstrument.stationList", strvector) + add("Observation.startTime") + add("Observation.stopTime") + add("Observation.nrBeams") + nrSAPs = int(get("Observation.nrBeams", 0)) + for sap in xrange(0, nrSAPs): + add("Observation.Beam[%d].subbandList" % (sap,), intvector) + + # ===================================== + # Correlator settings + # ===================================== + add("Observation.DataProducts.Output_Correlated.enabled", bool) + add("Cobalt.Correlator.integrationTime") + add("Cobalt.Correlator.nrChannelsPerSubband") + # TODO: We need a service that computes these 3 values + add("Cobalt.Correlator.nrBlocksPerIntegration") + add("Cobalt.Correlator.nrIntegrationsPerBlock") + add("Cobalt.blockSize") + + # ===================================== + # Beamformer settings + # ===================================== + add("Observation.DataProducts.Output_IncoherentStokes.enabled", bool) + add("Observation.DataProducts.Output_CoherentStokes.enabled", bool) + add("Cobalt.BeamFormer.flysEye", bool) + #add("Cobalt.BeamFormer.CoherentStokes.nrChannelsPerSubband") # only needed to determine Cobalt.blockSize + add("Cobalt.BeamFormer.CoherentStokes.subbandsPerFile") + add("Cobalt.BeamFormer.CoherentStokes.timeIntegrationFactor") + add("Cobalt.BeamFormer.CoherentStokes.which") + #add("Cobalt.BeamFormer.IncoherentStokes.nrChannelsPerSubband") # only needed to determine Cobalt.blockSize + add("Cobalt.BeamFormer.IncoherentStokes.subbandsPerFile") + add("Cobalt.BeamFormer.IncoherentStokes.timeIntegrationFactor") + add("Cobalt.BeamFormer.IncoherentStokes.which") + for sap in xrange(0, nrSAPs): + add("Observation.Beam[%d].nrTabRings" % (sap,)) + + nrTABs = int(get("Observation.Beam[%d].nrTiedArrayBeams" % (sap,), 0)) + for tab in xrange(0, nrTABs): + add("Observation.Beam[%d].TiedArrayBeam[%d].coherent" % (sap,tab), bool) + + # ===================================== + # Pipeline settings + # ===================================== + # Calibrator / Averaging pipelines + add("Observation.DataProducts.Output_Correlated.enabled", bool) + add("Observation.DataProducts.Output_InstrumentModel.enabled", bool) + add("Observation.DataProducts.Input_Correlated.enabled", bool) + add("Observation.DataProducts.Input_Correlated.skip", intvector) + add("Observation.ObservationControl.PythonControl.DPPP.demixer.demixfreqstep") + add("Observation.ObservationControl.PythonControl.DPPP.demixer.demixtimestep") + + # Imaging pipeline + add("Observation.DataProducts.Output_SkyImage.enabled", bool) + add("Observation.ObservationControl.PythonControl.Imaging.slices_per_image") + add("Observation.ObservationControl.PythonControl.Imaging.subbands_per_image") + + # Long-baseline pipeline + add("Observation.ObservationControl.PythonControl.LongBaseline.subbandgroups_per_ms") + add("Observation.ObservationControl.PythonControl.LongBaseline.subbands_per_subbandgroup") + + # Pulsar pipeline + add("Observation.DataProducts.Output_Pulsar.enabled", bool) + add("Observation.DataProducts.Input_CoherentStokes.enabled", bool) + add("Observation.DataProducts.Input_CoherentStokes.skip", intvector) + add("Observation.DataProducts.Input_IncoherentStokes.enabled", bool) + add("Observation.DataProducts.Input_IncoherentStokes.skip", intvector) + + return subset + +class RATaskSpecified(OTDBBusListener): + def __init__(self, servicename, otdb_busname=None, my_busname=None, **kwargs): + super(RATaskSpecified, self).__init__(busname=otdb_busname, subject="TaskStatus", **kwargs) + + self.parset_rpc = RPC(service="TaskSpecification", busname=otdb_busname) + self.send_bus = ToBus("%s/%s" % (my_busname, servicename)) + + def start_listening(self, **kwargs): + self.parset_rpc.open() + self.send_bus.open() + + super(RATaskSpecified, self).start_listening(**kwargs) + + def stop_listening(self, **kwargs): + super(RATaskSpecified, self).stop_listening(**kwargs) + + self.send_bus.close() + self.parset_rpc.close() + + def onObservationPrescheduled(self, treeId, modificationTime): + logger.info("Processing obs ID %s", treeId) + + # Request the parset + main_obsID = treeId + main_parset,_ = self.parset_rpc( OtdbID=main_obsID ) + + # Construct a dict of all the parsets we retrieved + parsets = {} + parsets[main_obsID] = main_parset + + logger.info("Processing predecessors") + + # Collect the initial set of predecessors + request_obsIDs = set(predecessors(main_parset)) + + logger.info("Processing %s", request_obsIDs) + + # Iterate recursively over all known predecessor obsIDs, and request their parsets + while request_obsIDs: + obsID = request_obsIDs.pop() + + if obsID in parsets: + # Predecessor lists can overlap -- we already have this one + continue + + logger.info("Fetching predecessor %s", obsID) + + # Request predecessor parset + parsets[obsID],_ = self.parset_rpc( OtdbID=obsID ) + + # Add the list of predecessors + request_obsIDs = request_obsIDs.union(predecessors(parsets[obsID])) + + # Convert parsets to resource indicators + logger.info("Extracting resource indicators") + resourceIndicators = dict([(str(obsID), resourceIndicatorsFromParset(parset)) for (obsID,parset) in parsets.iteritems()]) + + # Construct and send result message + logger.info("Sending result") + result = { + "sasID": main_obsID, + "state": "prescheduled", + "time_of_change": modificationTime, + "resource_indicators": resourceIndicators, + } + + # Put result on bus + msg = EventMessage(content=result) + self.send_bus.send(msg) + + logger.info("Result sent") + diff --git a/SAS/ResourceAssignment/Services/src/rataskspecifiedservice b/SAS/ResourceAssignment/Services/src/rataskspecifiedservice new file mode 100644 index 0000000000000000000000000000000000000000..bac878107d95e35d39803351ecd97b1debaad5eb --- /dev/null +++ b/SAS/ResourceAssignment/Services/src/rataskspecifiedservice @@ -0,0 +1,48 @@ +#!/usr/bin/env python +#coding: iso-8859-15 +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +# +# $Id: JobsToSchedule.py 33364 2016-01-21 21:21:12Z mol $ +""" +Daemon that listens to OTDB status changes to PRESCHEDULED, requests +the parset of such jobs (+ their predecessors), and posts them on the bus. +""" + +from lofar.sas.resourceassignment.RATaskSpecified import RATaskSpecified + +if __name__ == "__main__": + import sys + from optparse import OptionParser + + # Check the invocation arguments + parser = OptionParser("%prog -O otdb_bus -B my_bus [options]") + parser.add_option("-O", "--otdb_bus", dest="otdb_busname", type="string", default="", + help="Bus or queue OTDB operates on") + parser.add_option("-B", "--my_bus", dest="my_busname", type="string", default="", + help="Bus or queue we publish resource requests on") + (options, args) = parser.parse_args() + + if not options.statusbus or not options.parsetbus or not options.busname: + parser.print_help() + sys.exit(1) + + with RATaskSpecified("OTDB.TaskSpecified", otdb_busname=options.otdb_busname, my_busname=options.my_busname) as jts: + waitForInterrupt() + diff --git a/SAS/ResourceAssignment/Services/src/rataskspecifiedservice.ini b/SAS/ResourceAssignment/Services/src/rataskspecifiedservice.ini new file mode 100644 index 0000000000000000000000000000000000000000..23d43f56ee0fa5cc956418d5ee77c97030e952eb --- /dev/null +++ b/SAS/ResourceAssignment/Services/src/rataskspecifiedservice.ini @@ -0,0 +1,8 @@ +[program:RATaskSpecified] +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec rataskspecifiedservice' +user=lofarsys +stopsignal=INT ; KeyboardInterrupt +stopasgroup=true +stdout_logfile=%(program_name)s.log +redirect_stderr=true +stderr_logfile=NONE diff --git a/SAS/ResourceAssignment/Services/test/CMakeLists.txt b/SAS/ResourceAssignment/Services/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2dccf9d7337830a2811eb3f730a88e2f579f49d7 --- /dev/null +++ b/SAS/ResourceAssignment/Services/test/CMakeLists.txt @@ -0,0 +1,5 @@ +# $Id$ + +include(LofarCTest) + +lofar_add_test(tRATaskSpecified) diff --git a/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_correlator b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_correlator new file mode 100644 index 0000000000000000000000000000000000000000..065b4a4adcfe512ed30602d7f4a5080e579e40c0 --- /dev/null +++ b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_correlator @@ -0,0 +1,356 @@ +ApplCtrl.application=CorrAppl +ApplCtrl.processes=[CorrProc] +ApplCtrl.resultfile=/opt/lofar/var/run/ACC_CCU001:OnlineControl[0]{427282}_CorrAppl_result.param +Cobalt.BeamFormer.CoherentStokes.nrChannelsPerSubband=1 +Cobalt.BeamFormer.CoherentStokes.subbandsPerFile=512 +Cobalt.BeamFormer.CoherentStokes.timeIntegrationFactor=1 +Cobalt.BeamFormer.CoherentStokes.which=I +Cobalt.BeamFormer.IncoherentStokes.nrChannelsPerSubband=1 +Cobalt.BeamFormer.IncoherentStokes.subbandsPerFile=512 +Cobalt.BeamFormer.IncoherentStokes.timeIntegrationFactor=1 +Cobalt.BeamFormer.IncoherentStokes.which=I +Cobalt.BeamFormer.coherentDedisperseChannels=false +Cobalt.BeamFormer.flysEye=false +Cobalt.Correlator.integrationTime=1.00139 +Cobalt.Correlator.nrBlocksPerIntegration=1 +Cobalt.Correlator.nrChannelsPerSubband=64 +Cobalt.Correlator.nrIntegrationsPerBlock=1 +Cobalt.blockSize=195584 +Cobalt.correctBandPass=true +Cobalt.correctClocks=true +Cobalt.delayCompensation=true +Cobalt.realTime=true +CorrAppl.CorrProc._executable=CN_Processing +CorrAppl.CorrProc._hostname=cbmmaster +CorrAppl.CorrProc._nodes=[] +CorrAppl.CorrProc._startstopType=bgl +CorrAppl.CorrProc.workingdir=/opt/lofar/bin/ +CorrAppl._hostname=cbmmaster +CorrAppl.extraInfo=["PIC","Cobalt"] +CorrAppl.procesOrder=[] +CorrAppl.processes=["CorrProc"] +Observation.AnaBeam[0].angle1=3.71468 +Observation.AnaBeam[0].angle2=0.91111 +Observation.AnaBeam[0].directionType=J2000 +Observation.AnaBeam[0].duration=0 +Observation.AnaBeam[0].rank=1 +Observation.AnaBeam[0].startTime=0 +Observation.AnaBeam[0].target= +Observation.Beam[0].TiedArrayBeam.angle1=0 +Observation.Beam[0].TiedArrayBeam.angle2=0 +Observation.Beam[0].TiedArrayBeam.coherent=true +Observation.Beam[0].TiedArrayBeam.directionType=J2000 +Observation.Beam[0].TiedArrayBeam.dispersionMeasure=0 +Observation.Beam[0].angle1=3.71468 +Observation.Beam[0].angle2=0.91111 +Observation.Beam[0].directionType=J2000 +Observation.Beam[0].duration=0 +Observation.Beam[0].momID=0 +Observation.Beam[0].nrTabRings=0 +Observation.Beam[0].nrTiedArrayBeams=0 +Observation.Beam[0].startTime=0 +Observation.Beam[0].subbandList=[52..255] +Observation.Beam[0].tabRingSize=0 +Observation.Beam[0].target=3C 295 +Observation.Campaign.CO_I="" +Observation.Campaign.PI="Pizzo, Dr. Roberto Francesco" +Observation.Campaign.contact="Pizzo, Dr. Roberto Francesco" +Observation.Campaign.name="2015LOFAROBS_new" +Observation.Campaign.title="2015LOFAROBS_new" +Observation.DataProducts.Input_CoherentStokes.dirmask= +Observation.DataProducts.Input_CoherentStokes.enabled=false +Observation.DataProducts.Input_CoherentStokes.filenames=[] +Observation.DataProducts.Input_CoherentStokes.identifications=[] +Observation.DataProducts.Input_CoherentStokes.locations=[] +Observation.DataProducts.Input_CoherentStokes.mountpoints=[] +Observation.DataProducts.Input_CoherentStokes.namemask= +Observation.DataProducts.Input_CoherentStokes.skip=[] +Observation.DataProducts.Input_Correlated.dirmask= +Observation.DataProducts.Input_Correlated.enabled=false +Observation.DataProducts.Input_Correlated.filenames=[] +Observation.DataProducts.Input_Correlated.identifications=[] +Observation.DataProducts.Input_Correlated.locations=[] +Observation.DataProducts.Input_Correlated.mountpoints=[] +Observation.DataProducts.Input_Correlated.namemask= +Observation.DataProducts.Input_Correlated.skip=[] +Observation.DataProducts.Input_IncoherentStokes.dirmask= +Observation.DataProducts.Input_IncoherentStokes.enabled=false +Observation.DataProducts.Input_IncoherentStokes.filenames=[] +Observation.DataProducts.Input_IncoherentStokes.identifications=[] +Observation.DataProducts.Input_IncoherentStokes.locations=[] +Observation.DataProducts.Input_IncoherentStokes.mountpoints=[] +Observation.DataProducts.Input_IncoherentStokes.namemask= +Observation.DataProducts.Input_IncoherentStokes.skip=[] +Observation.DataProducts.Input_InstrumentModel.dirmask= +Observation.DataProducts.Input_InstrumentModel.enabled=false +Observation.DataProducts.Input_InstrumentModel.filenames=[] +Observation.DataProducts.Input_InstrumentModel.identifications=[] +Observation.DataProducts.Input_InstrumentModel.locations=[] +Observation.DataProducts.Input_InstrumentModel.mountpoints=[] +Observation.DataProducts.Input_InstrumentModel.namemask= +Observation.DataProducts.Input_InstrumentModel.skip=[] +Observation.DataProducts.Input_SkyImage.dirmask= +Observation.DataProducts.Input_SkyImage.enabled=false +Observation.DataProducts.Input_SkyImage.filenames=[] +Observation.DataProducts.Input_SkyImage.identifications=[] +Observation.DataProducts.Input_SkyImage.locations=[] +Observation.DataProducts.Input_SkyImage.mountpoints=[] +Observation.DataProducts.Input_SkyImage.namemask= +Observation.DataProducts.Input_SkyImage.skip=[] +Observation.DataProducts.Output_CoherentStokes.archived=false +Observation.DataProducts.Output_CoherentStokes.deleted=false +Observation.DataProducts.Output_CoherentStokes.dirmask= +Observation.DataProducts.Output_CoherentStokes.enabled=false +Observation.DataProducts.Output_CoherentStokes.filenames=[] +Observation.DataProducts.Output_CoherentStokes.identifications=[] +Observation.DataProducts.Output_CoherentStokes.locations=[] +Observation.DataProducts.Output_CoherentStokes.mountpoints=[] +Observation.DataProducts.Output_CoherentStokes.namemask= +Observation.DataProducts.Output_CoherentStokes.percentageWritten=[] +Observation.DataProducts.Output_CoherentStokes.retentiontime=14 +Observation.DataProducts.Output_CoherentStokes.skip=[] +Observation.DataProducts.Output_Correlated.archived=false +Observation.DataProducts.Output_Correlated.deleted=false +Observation.DataProducts.Output_Correlated.dirmask=L${OBSID} +Observation.DataProducts.Output_Correlated.enabled=true +Observation.DataProducts.Output_Correlated.filenames=[L427282_SAP000_SB000_uv.MS,L427282_SAP000_SB001_uv.MS,L427282_SAP000_SB002_uv.MS,L427282_SAP000_SB003_uv.MS,L427282_SAP000_SB004_uv.MS,L427282_SAP000_SB005_uv.MS,L427282_SAP000_SB006_uv.MS,L427282_SAP000_SB007_uv.MS,L427282_SAP000_SB008_uv.MS,L427282_SAP000_SB009_uv.MS,L427282_SAP000_SB010_uv.MS,L427282_SAP000_SB011_uv.MS,L427282_SAP000_SB012_uv.MS,L427282_SAP000_SB013_uv.MS,L427282_SAP000_SB014_uv.MS,L427282_SAP000_SB015_uv.MS,L427282_SAP000_SB016_uv.MS,L427282_SAP000_SB017_uv.MS,L427282_SAP000_SB018_uv.MS,L427282_SAP000_SB019_uv.MS,L427282_SAP000_SB020_uv.MS,L427282_SAP000_SB021_uv.MS,L427282_SAP000_SB022_uv.MS,L427282_SAP000_SB023_uv.MS,L427282_SAP000_SB024_uv.MS,L427282_SAP000_SB025_uv.MS,L427282_SAP000_SB026_uv.MS,L427282_SAP000_SB027_uv.MS,L427282_SAP000_SB028_uv.MS,L427282_SAP000_SB029_uv.MS,L427282_SAP000_SB030_uv.MS,L427282_SAP000_SB031_uv.MS,L427282_SAP000_SB032_uv.MS,L427282_SAP000_SB033_uv.MS,L427282_SAP000_SB034_uv.MS,L427282_SAP000_SB035_uv.MS,L427282_SAP000_SB036_uv.MS,L427282_SAP000_SB037_uv.MS,L427282_SAP000_SB038_uv.MS,L427282_SAP000_SB039_uv.MS,L427282_SAP000_SB040_uv.MS,L427282_SAP000_SB041_uv.MS,L427282_SAP000_SB042_uv.MS,L427282_SAP000_SB043_uv.MS,L427282_SAP000_SB044_uv.MS,L427282_SAP000_SB045_uv.MS,L427282_SAP000_SB046_uv.MS,L427282_SAP000_SB047_uv.MS,L427282_SAP000_SB048_uv.MS,L427282_SAP000_SB049_uv.MS,L427282_SAP000_SB050_uv.MS,L427282_SAP000_SB051_uv.MS,L427282_SAP000_SB052_uv.MS,L427282_SAP000_SB053_uv.MS,L427282_SAP000_SB054_uv.MS,L427282_SAP000_SB055_uv.MS,L427282_SAP000_SB056_uv.MS,L427282_SAP000_SB057_uv.MS,L427282_SAP000_SB058_uv.MS,L427282_SAP000_SB059_uv.MS,L427282_SAP000_SB060_uv.MS,L427282_SAP000_SB061_uv.MS,L427282_SAP000_SB062_uv.MS,L427282_SAP000_SB063_uv.MS,L427282_SAP000_SB064_uv.MS,L427282_SAP000_SB065_uv.MS,L427282_SAP000_SB066_uv.MS,L427282_SAP000_SB067_uv.MS,L427282_SAP000_SB068_uv.MS,L427282_SAP000_SB069_uv.MS,L427282_SAP000_SB070_uv.MS,L427282_SAP000_SB071_uv.MS,L427282_SAP000_SB072_uv.MS,L427282_SAP000_SB073_uv.MS,L427282_SAP000_SB074_uv.MS,L427282_SAP000_SB075_uv.MS,L427282_SAP000_SB076_uv.MS,L427282_SAP000_SB077_uv.MS,L427282_SAP000_SB078_uv.MS,L427282_SAP000_SB079_uv.MS,L427282_SAP000_SB080_uv.MS,L427282_SAP000_SB081_uv.MS,L427282_SAP000_SB082_uv.MS,L427282_SAP000_SB083_uv.MS,L427282_SAP000_SB084_uv.MS,L427282_SAP000_SB085_uv.MS,L427282_SAP000_SB086_uv.MS,L427282_SAP000_SB087_uv.MS,L427282_SAP000_SB088_uv.MS,L427282_SAP000_SB089_uv.MS,L427282_SAP000_SB090_uv.MS,L427282_SAP000_SB091_uv.MS,L427282_SAP000_SB092_uv.MS,L427282_SAP000_SB093_uv.MS,L427282_SAP000_SB094_uv.MS,L427282_SAP000_SB095_uv.MS,L427282_SAP000_SB096_uv.MS,L427282_SAP000_SB097_uv.MS,L427282_SAP000_SB098_uv.MS,L427282_SAP000_SB099_uv.MS,L427282_SAP000_SB100_uv.MS,L427282_SAP000_SB101_uv.MS,L427282_SAP000_SB102_uv.MS,L427282_SAP000_SB103_uv.MS,L427282_SAP000_SB104_uv.MS,L427282_SAP000_SB105_uv.MS,L427282_SAP000_SB106_uv.MS,L427282_SAP000_SB107_uv.MS,L427282_SAP000_SB108_uv.MS,L427282_SAP000_SB109_uv.MS,L427282_SAP000_SB110_uv.MS,L427282_SAP000_SB111_uv.MS,L427282_SAP000_SB112_uv.MS,L427282_SAP000_SB113_uv.MS,L427282_SAP000_SB114_uv.MS,L427282_SAP000_SB115_uv.MS,L427282_SAP000_SB116_uv.MS,L427282_SAP000_SB117_uv.MS,L427282_SAP000_SB118_uv.MS,L427282_SAP000_SB119_uv.MS,L427282_SAP000_SB120_uv.MS,L427282_SAP000_SB121_uv.MS,L427282_SAP000_SB122_uv.MS,L427282_SAP000_SB123_uv.MS,L427282_SAP000_SB124_uv.MS,L427282_SAP000_SB125_uv.MS,L427282_SAP000_SB126_uv.MS,L427282_SAP000_SB127_uv.MS,L427282_SAP000_SB128_uv.MS,L427282_SAP000_SB129_uv.MS,L427282_SAP000_SB130_uv.MS,L427282_SAP000_SB131_uv.MS,L427282_SAP000_SB132_uv.MS,L427282_SAP000_SB133_uv.MS,L427282_SAP000_SB134_uv.MS,L427282_SAP000_SB135_uv.MS,L427282_SAP000_SB136_uv.MS,L427282_SAP000_SB137_uv.MS,L427282_SAP000_SB138_uv.MS,L427282_SAP000_SB139_uv.MS,L427282_SAP000_SB140_uv.MS,L427282_SAP000_SB141_uv.MS,L427282_SAP000_SB142_uv.MS,L427282_SAP000_SB143_uv.MS,L427282_SAP000_SB144_uv.MS,L427282_SAP000_SB145_uv.MS,L427282_SAP000_SB146_uv.MS,L427282_SAP000_SB147_uv.MS,L427282_SAP000_SB148_uv.MS,L427282_SAP000_SB149_uv.MS,L427282_SAP000_SB150_uv.MS,L427282_SAP000_SB151_uv.MS,L427282_SAP000_SB152_uv.MS,L427282_SAP000_SB153_uv.MS,L427282_SAP000_SB154_uv.MS,L427282_SAP000_SB155_uv.MS,L427282_SAP000_SB156_uv.MS,L427282_SAP000_SB157_uv.MS,L427282_SAP000_SB158_uv.MS,L427282_SAP000_SB159_uv.MS,L427282_SAP000_SB160_uv.MS,L427282_SAP000_SB161_uv.MS,L427282_SAP000_SB162_uv.MS,L427282_SAP000_SB163_uv.MS,L427282_SAP000_SB164_uv.MS,L427282_SAP000_SB165_uv.MS,L427282_SAP000_SB166_uv.MS,L427282_SAP000_SB167_uv.MS,L427282_SAP000_SB168_uv.MS,L427282_SAP000_SB169_uv.MS,L427282_SAP000_SB170_uv.MS,L427282_SAP000_SB171_uv.MS,L427282_SAP000_SB172_uv.MS,L427282_SAP000_SB173_uv.MS,L427282_SAP000_SB174_uv.MS,L427282_SAP000_SB175_uv.MS,L427282_SAP000_SB176_uv.MS,L427282_SAP000_SB177_uv.MS,L427282_SAP000_SB178_uv.MS,L427282_SAP000_SB179_uv.MS,L427282_SAP000_SB180_uv.MS,L427282_SAP000_SB181_uv.MS,L427282_SAP000_SB182_uv.MS,L427282_SAP000_SB183_uv.MS,L427282_SAP000_SB184_uv.MS,L427282_SAP000_SB185_uv.MS,L427282_SAP000_SB186_uv.MS,L427282_SAP000_SB187_uv.MS,L427282_SAP000_SB188_uv.MS,L427282_SAP000_SB189_uv.MS,L427282_SAP000_SB190_uv.MS,L427282_SAP000_SB191_uv.MS,L427282_SAP000_SB192_uv.MS,L427282_SAP000_SB193_uv.MS,L427282_SAP000_SB194_uv.MS,L427282_SAP000_SB195_uv.MS,L427282_SAP000_SB196_uv.MS,L427282_SAP000_SB197_uv.MS,L427282_SAP000_SB198_uv.MS,L427282_SAP000_SB199_uv.MS,L427282_SAP000_SB200_uv.MS,L427282_SAP000_SB201_uv.MS,L427282_SAP000_SB202_uv.MS,L427282_SAP000_SB203_uv.MS] +Observation.DataProducts.Output_Correlated.identifications=[] +Observation.DataProducts.Output_Correlated.locations=[locus001:/data/L427282/,locus004:/data/L427282/,locus005:/data/L427282/,locus006:/data/L427282/,locus007:/data/L427282/,locus008:/data/L427282/,locus009:/data/L427282/,locus010:/data/L427282/,locus011:/data/L427282/,locus012:/data/L427282/,locus014:/data/L427282/,locus016:/data/L427282/,locus017:/data/L427282/,locus018:/data/L427282/,locus020:/data/L427282/,locus021:/data/L427282/,locus023:/data/L427282/,locus025:/data/L427282/,locus026:/data/L427282/,locus027:/data/L427282/,locus028:/data/L427282/,locus029:/data/L427282/,locus031:/data/L427282/,locus032:/data/L427282/,locus034:/data/L427282/,locus035:/data/L427282/,locus036:/data/L427282/,locus037:/data/L427282/,locus038:/data/L427282/,locus039:/data/L427282/,locus040:/data/L427282/,locus041:/data/L427282/,locus042:/data/L427282/,locus043:/data/L427282/,locus044:/data/L427282/,locus045:/data/L427282/,locus046:/data/L427282/,locus047:/data/L427282/,locus048:/data/L427282/,locus049:/data/L427282/,locus050:/data/L427282/,locus051:/data/L427282/,locus052:/data/L427282/,locus053:/data/L427282/,locus054:/data/L427282/,locus055:/data/L427282/,locus056:/data/L427282/,locus057:/data/L427282/,locus058:/data/L427282/,locus060:/data/L427282/,locus061:/data/L427282/,locus062:/data/L427282/,locus063:/data/L427282/,locus064:/data/L427282/,locus065:/data/L427282/,locus066:/data/L427282/,locus067:/data/L427282/,locus068:/data/L427282/,locus069:/data/L427282/,locus070:/data/L427282/,locus071:/data/L427282/,locus072:/data/L427282/,locus073:/data/L427282/,locus074:/data/L427282/,locus075:/data/L427282/,locus076:/data/L427282/,locus077:/data/L427282/,locus078:/data/L427282/,locus079:/data/L427282/,locus080:/data/L427282/,locus081:/data/L427282/,locus082:/data/L427282/,locus084:/data/L427282/,locus085:/data/L427282/,locus086:/data/L427282/,locus087:/data/L427282/,locus088:/data/L427282/,locus089:/data/L427282/,locus090:/data/L427282/,locus091:/data/L427282/,locus001:/data/L427282/,locus004:/data/L427282/,locus005:/data/L427282/,locus006:/data/L427282/,locus007:/data/L427282/,locus008:/data/L427282/,locus009:/data/L427282/,locus010:/data/L427282/,locus011:/data/L427282/,locus012:/data/L427282/,locus014:/data/L427282/,locus016:/data/L427282/,locus017:/data/L427282/,locus018:/data/L427282/,locus020:/data/L427282/,locus021:/data/L427282/,locus023:/data/L427282/,locus025:/data/L427282/,locus026:/data/L427282/,locus027:/data/L427282/,locus028:/data/L427282/,locus029:/data/L427282/,locus031:/data/L427282/,locus032:/data/L427282/,locus034:/data/L427282/,locus035:/data/L427282/,locus036:/data/L427282/,locus037:/data/L427282/,locus038:/data/L427282/,locus039:/data/L427282/,locus040:/data/L427282/,locus041:/data/L427282/,locus042:/data/L427282/,locus043:/data/L427282/,locus044:/data/L427282/,locus045:/data/L427282/,locus046:/data/L427282/,locus047:/data/L427282/,locus048:/data/L427282/,locus049:/data/L427282/,locus050:/data/L427282/,locus051:/data/L427282/,locus052:/data/L427282/,locus053:/data/L427282/,locus054:/data/L427282/,locus055:/data/L427282/,locus056:/data/L427282/,locus057:/data/L427282/,locus058:/data/L427282/,locus060:/data/L427282/,locus061:/data/L427282/,locus062:/data/L427282/,locus063:/data/L427282/,locus064:/data/L427282/,locus065:/data/L427282/,locus066:/data/L427282/,locus067:/data/L427282/,locus068:/data/L427282/,locus069:/data/L427282/,locus070:/data/L427282/,locus071:/data/L427282/,locus072:/data/L427282/,locus073:/data/L427282/,locus074:/data/L427282/,locus075:/data/L427282/,locus076:/data/L427282/,locus077:/data/L427282/,locus078:/data/L427282/,locus079:/data/L427282/,locus080:/data/L427282/,locus081:/data/L427282/,locus082:/data/L427282/,locus084:/data/L427282/,locus085:/data/L427282/,locus086:/data/L427282/,locus087:/data/L427282/,locus088:/data/L427282/,locus089:/data/L427282/,locus090:/data/L427282/,locus091:/data/L427282/,locus001:/data/L427282/,locus004:/data/L427282/,locus005:/data/L427282/,locus006:/data/L427282/,locus007:/data/L427282/,locus008:/data/L427282/,locus009:/data/L427282/,locus010:/data/L427282/,locus011:/data/L427282/,locus012:/data/L427282/,locus014:/data/L427282/,locus016:/data/L427282/,locus017:/data/L427282/,locus018:/data/L427282/,locus020:/data/L427282/,locus021:/data/L427282/,locus023:/data/L427282/,locus025:/data/L427282/,locus026:/data/L427282/,locus027:/data/L427282/,locus028:/data/L427282/,locus029:/data/L427282/,locus031:/data/L427282/,locus032:/data/L427282/,locus034:/data/L427282/,locus035:/data/L427282/,locus036:/data/L427282/,locus037:/data/L427282/,locus038:/data/L427282/,locus039:/data/L427282/,locus040:/data/L427282/,locus041:/data/L427282/,locus042:/data/L427282/,locus043:/data/L427282/,locus044:/data/L427282/,locus045:/data/L427282/,locus046:/data/L427282/,locus047:/data/L427282/,locus048:/data/L427282/,locus049:/data/L427282/,locus050:/data/L427282/,locus051:/data/L427282/,locus052:/data/L427282/,locus053:/data/L427282/] +Observation.DataProducts.Output_Correlated.mountpoints=[locus001:/data,locus004:/data,locus005:/data,locus006:/data,locus007:/data,locus008:/data,locus009:/data,locus010:/data,locus011:/data,locus012:/data,locus014:/data,locus016:/data,locus017:/data,locus018:/data,locus020:/data,locus021:/data,locus023:/data,locus025:/data,locus026:/data,locus027:/data,locus028:/data,locus029:/data,locus031:/data,locus032:/data,locus034:/data,locus035:/data,locus036:/data,locus037:/data,locus038:/data,locus039:/data,locus040:/data,locus041:/data,locus042:/data,locus043:/data,locus044:/data,locus045:/data,locus046:/data,locus047:/data,locus048:/data,locus049:/data,locus050:/data,locus051:/data,locus052:/data,locus053:/data,locus054:/data,locus055:/data,locus056:/data,locus057:/data,locus058:/data,locus060:/data,locus061:/data,locus062:/data,locus063:/data,locus064:/data,locus065:/data,locus066:/data,locus067:/data,locus068:/data,locus069:/data,locus070:/data,locus071:/data,locus072:/data,locus073:/data,locus074:/data,locus075:/data,locus076:/data,locus077:/data,locus078:/data,locus079:/data,locus080:/data,locus081:/data,locus082:/data,locus084:/data,locus085:/data,locus086:/data,locus087:/data,locus088:/data,locus089:/data,locus090:/data,locus091:/data] +Observation.DataProducts.Output_Correlated.namemask=L${OBSID}_SAP${SAP}_SB${SUBBAND}_uv.MS +Observation.DataProducts.Output_Correlated.percentageWritten=[] +Observation.DataProducts.Output_Correlated.retentiontime=14 +Observation.DataProducts.Output_Correlated.skip=[] +Observation.DataProducts.Output_IncoherentStokes.archived=false +Observation.DataProducts.Output_IncoherentStokes.deleted=false +Observation.DataProducts.Output_IncoherentStokes.dirmask= +Observation.DataProducts.Output_IncoherentStokes.enabled=false +Observation.DataProducts.Output_IncoherentStokes.filenames=[] +Observation.DataProducts.Output_IncoherentStokes.identifications=[] +Observation.DataProducts.Output_IncoherentStokes.locations=[] +Observation.DataProducts.Output_IncoherentStokes.mountpoints=[] +Observation.DataProducts.Output_IncoherentStokes.namemask= +Observation.DataProducts.Output_IncoherentStokes.percentageWritten=[] +Observation.DataProducts.Output_IncoherentStokes.retentiontime=14 +Observation.DataProducts.Output_IncoherentStokes.skip=[] +Observation.DataProducts.Output_InstrumentModel.archived=false +Observation.DataProducts.Output_InstrumentModel.deleted=false +Observation.DataProducts.Output_InstrumentModel.dirmask= +Observation.DataProducts.Output_InstrumentModel.enabled=false +Observation.DataProducts.Output_InstrumentModel.filenames=[] +Observation.DataProducts.Output_InstrumentModel.identifications=[] +Observation.DataProducts.Output_InstrumentModel.locations=[] +Observation.DataProducts.Output_InstrumentModel.mountpoints=[] +Observation.DataProducts.Output_InstrumentModel.namemask= +Observation.DataProducts.Output_InstrumentModel.percentageWritten=[] +Observation.DataProducts.Output_InstrumentModel.retentiontime=14 +Observation.DataProducts.Output_InstrumentModel.skip=[] +Observation.DataProducts.Output_Pulsar.archived=false +Observation.DataProducts.Output_Pulsar.deleted=false +Observation.DataProducts.Output_Pulsar.dirmask= +Observation.DataProducts.Output_Pulsar.enabled=false +Observation.DataProducts.Output_Pulsar.filenames=[] +Observation.DataProducts.Output_Pulsar.identifications=[] +Observation.DataProducts.Output_Pulsar.locations=[] +Observation.DataProducts.Output_Pulsar.mountpoints=[] +Observation.DataProducts.Output_Pulsar.namemask= +Observation.DataProducts.Output_Pulsar.percentageWritten=[] +Observation.DataProducts.Output_Pulsar.retentiontime=14 +Observation.DataProducts.Output_Pulsar.skip=[] +Observation.DataProducts.Output_SkyImage.archived=false +Observation.DataProducts.Output_SkyImage.deleted=false +Observation.DataProducts.Output_SkyImage.dirmask= +Observation.DataProducts.Output_SkyImage.enabled=false +Observation.DataProducts.Output_SkyImage.filenames=[] +Observation.DataProducts.Output_SkyImage.identifications=[] +Observation.DataProducts.Output_SkyImage.locations=[] +Observation.DataProducts.Output_SkyImage.mountpoints=[] +Observation.DataProducts.Output_SkyImage.namemask= +Observation.DataProducts.Output_SkyImage.percentageWritten=[] +Observation.DataProducts.Output_SkyImage.retentiontime=14 +Observation.DataProducts.Output_SkyImage.skip=[] +Observation.Dataslots.CS001HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS001HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS002HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS002HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS003HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS003HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS004HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS004HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS005HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS005HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS006HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS006HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS007HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS007HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS011HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS011HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS013HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS013HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS017HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS017HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS021HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS021HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS024HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS024HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS026HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS026HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS028HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS028HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS030HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS030HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS031HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS031HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS032HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS032HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS101HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS101HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS103HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS103HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS201HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS201HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS301HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS301HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS302HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS302HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS401HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS401HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.CS501HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.CS501HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.DataslotInfo.DataslotList=[] +Observation.Dataslots.DataslotInfo.RSPBoardList=[] +Observation.Dataslots.RS106HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS106HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS205HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS205HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS208HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS208HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS210HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS210HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS305HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS305HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS306HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS306HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS307HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS307HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS310HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS310HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS406HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS406HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS407HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS407HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS409HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS409HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS503HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS503HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.Dataslots.RS508HBA.DataslotList=[0..60,0..60,0..60,0..20] +Observation.Dataslots.RS508HBA.RSPBoardList=[61*0,61*1,61*2,21*3] +Observation.ObsID=427282 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.nrChannelsPerSubband=1 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.subbandsPerFile=512 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.timeIntegrationFactor=1 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.CoherentStokes.which=I +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.IncoherentStokes.nrChannelsPerSubband=1 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.IncoherentStokes.subbandsPerFile=512 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.IncoherentStokes.timeIntegrationFactor=1 +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.IncoherentStokes.which=I +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.coherentDedisperseChannels=false +Observation.ObservationControl.OnlineControl.Cobalt.BeamFormer.flysEye=false +Observation.ObservationControl.OnlineControl.Cobalt.Correlator.integrationTime=1.00139 +Observation.ObservationControl.OnlineControl.Cobalt.Correlator.nrBlocksPerIntegration=1 +Observation.ObservationControl.OnlineControl.Cobalt.Correlator.nrChannelsPerSubband=64 +Observation.ObservationControl.OnlineControl.Cobalt.Correlator.nrIntegrationsPerBlock=1 +Observation.ObservationControl.OnlineControl.Cobalt.blockSize=195584 +Observation.ObservationControl.OnlineControl.Cobalt.correctBandPass=true +Observation.ObservationControl.OnlineControl.Cobalt.correctClocks=true +Observation.ObservationControl.OnlineControl.Cobalt.delayCompensation=true +Observation.ObservationControl.OnlineControl.Cobalt.realTime=true +Observation.ObservationControl.OnlineControl.CorrAppl.CorrProc._executable=CN_Processing +Observation.ObservationControl.OnlineControl.CorrAppl.CorrProc._hostname=cbmmaster +Observation.ObservationControl.OnlineControl.CorrAppl.CorrProc._nodes=[] +Observation.ObservationControl.OnlineControl.CorrAppl.CorrProc._startstopType=bgl +Observation.ObservationControl.OnlineControl.CorrAppl.CorrProc.workingdir=/opt/lofar/bin/ +Observation.ObservationControl.OnlineControl.CorrAppl._hostname=cbmmaster +Observation.ObservationControl.OnlineControl.CorrAppl.extraInfo=["PIC","Cobalt"] +Observation.ObservationControl.OnlineControl.CorrAppl.procesOrder=[] +Observation.ObservationControl.OnlineControl.CorrAppl.processes=["CorrProc"] +Observation.ObservationControl.OnlineControl._hostname=CCU001 +Observation.ObservationControl.OnlineControl.applOrder=["CorrAppl"] +Observation.ObservationControl.OnlineControl.applications=["CorrAppl"] +Observation.ObservationControl.OnlineControl.inspectionHost=lhn001.cep2.lofar +Observation.ObservationControl.OnlineControl.inspectionProgram=/opt/cep/pyautoplot/bin/launch-msplots.sh +Observation.ObservationControl.StationControl.TBBControl.CoincidenceTime=0 +Observation.ObservationControl.StationControl.TBBControl.DoDirectionFit=none +Observation.ObservationControl.StationControl.TBBControl.MaxFitVariance=0 +Observation.ObservationControl.StationControl.TBBControl.MinElevation=0 +Observation.ObservationControl.StationControl.TBBControl.NoCoincChann=0 +Observation.ObservationControl.StationControl.TBBControl.ParamExtension= +Observation.ObservationControl.StationControl._hostname=[CS001,CS002,CS003,CS004,CS005,CS006,CS007,CS011,CS013,CS017,CS021,CS024,CS026,CS028,CS030,CS031,CS032,CS101,CS103,CS201,CS301,CS302,CS401,CS501,RS106,RS205,RS208,RS210,RS305,RS306,RS307,RS310,RS406,RS407,RS409,RS503,RS508] +Observation.ObservationControl.StationControl.aartfaacPiggybackAllowed=true +Observation.ObservationControl.StationControl.tbbPiggybackAllowed=true +Observation.ObservationControl._hostname=MCU001 +Observation.ObservationControl.heartbeatInterval=10 +Observation.Scheduler.contactEmail= +Observation.Scheduler.contactName=Pizzo, Dr. Roberto Francesco +Observation.Scheduler.contactPhone= +Observation.Scheduler.firstPossibleDay=5864 +Observation.Scheduler.fixedDay=false +Observation.Scheduler.fixedTime=false +Observation.Scheduler.lastPossibleDay=6049 +Observation.Scheduler.late= +Observation.Scheduler.nightTimeWeightFactor=4 +Observation.Scheduler.predMaxTimeDif=0000:00:00 +Observation.Scheduler.predMinTimeDif=0000:00:00 +Observation.Scheduler.predecessors=[] +Observation.Scheduler.priority=0 +Observation.Scheduler.reason= +Observation.Scheduler.referenceFrame=J2000 +Observation.Scheduler.reservation=0 +Observation.Scheduler.storageSelectionMode=1 +Observation.Scheduler.taskDuration=120 +Observation.Scheduler.taskID=532 +Observation.Scheduler.taskName=(copy of) (copy of) XC HBA_JOINED +Observation.Scheduler.taskType=0 +Observation.Scheduler.windowMaximumTime=23:59:59 +Observation.Scheduler.windowMinimumTime=00:00:00 +Observation.TBB.TBBsetting.RCUs=[] +Observation.TBB.TBBsetting.baselevel=127 +Observation.TBB.TBBsetting.filter=0 +Observation.TBB.TBBsetting.filter0_coeff0=0 +Observation.TBB.TBBsetting.filter0_coeff1=0 +Observation.TBB.TBBsetting.filter0_coeff2=0 +Observation.TBB.TBBsetting.filter0_coeff3=0 +Observation.TBB.TBBsetting.filter1_coeff0=0 +Observation.TBB.TBBsetting.filter1_coeff1=0 +Observation.TBB.TBBsetting.filter1_coeff2=0 +Observation.TBB.TBBsetting.filter1_coeff3=0 +Observation.TBB.TBBsetting.operatingMode=1 +Observation.TBB.TBBsetting.startlevel=7 +Observation.TBB.TBBsetting.stoplevel=7 +Observation.TBB.TBBsetting.subbandList=[] +Observation.TBB.TBBsetting.triggerMode=1 +Observation.TBB.TBBsetting.window=1M +Observation.VirtualInstrument.minimalNrStations=1 +Observation.VirtualInstrument.stationList=[CS001,CS002,CS003,CS004,CS005,CS006,CS007,CS011,CS013,CS017,CS021,CS024,CS026,CS028,CS030,CS031,CS032,CS101,CS103,CS201,CS301,CS302,CS401,CS501,RS106,RS205,RS208,RS210,RS305,RS306,RS307,RS310,RS406,RS407,RS409,RS503,RS508] +Observation.VirtualInstrument.stationSet= +Observation.antennaArray=HBA +Observation.antennaSet=HBA_JOINED +Observation.bandFilter=HBA_210_250 +Observation.claimPeriod=35 +Observation.clockMode=<<Clock200 +Observation.momID=0 +Observation.nrAnaBeams=1 +Observation.nrBeams=1 +Observation.nrBitsPerSample=16 +Observation.nrTBBSettings=0 +Observation.originID=254020 +Observation.otdbID=427282 +Observation.preparePeriod=20 +Observation.processSubtype=Beam Observation +Observation.processType=Observation +Observation.sampleClock=200 +Observation.startTime=2016-01-21 14:30:00 +Observation.stopTime=2016-01-21 14:32:00 +Observation.strategy=default +Observation.topologyID= +_DPname=LOFAR_ObsSW_TempObs0054 diff --git a/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_preprocessing b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_preprocessing new file mode 100644 index 0000000000000000000000000000000000000000..88bf9567fb31a55e9a86fbc60632d3df2e1c8a98 --- /dev/null +++ b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.in_preprocessing @@ -0,0 +1,273 @@ +Clock160.channelWidth=610.3515625 +Clock160.samplesPerSecond=155648 +Clock160.subbandWidth=156.250 +Clock160.systemClock=160 +Clock200.channelWidth=762.939453125 +Clock200.samplesPerSecond=196608 +Clock200.subbandWidth=195.3125 +Clock200.systemClock=200 +ObsSW.Observation.Campaign.CO_I="" +ObsSW.Observation.Campaign.PI="Pizzo, Dr. Roberto Francesco" +ObsSW.Observation.Campaign.contact="Pizzo, Dr. Roberto Francesco" +ObsSW.Observation.Campaign.name="2015LOFAROBS_new" +ObsSW.Observation.Campaign.title="2015LOFAROBS_new" +ObsSW.Observation.DataProducts.Input_CoherentStokes.dirmask= +ObsSW.Observation.DataProducts.Input_CoherentStokes.enabled=false +ObsSW.Observation.DataProducts.Input_CoherentStokes.filenames=[] +ObsSW.Observation.DataProducts.Input_CoherentStokes.identifications=[] +ObsSW.Observation.DataProducts.Input_CoherentStokes.locations=[] +ObsSW.Observation.DataProducts.Input_CoherentStokes.mountpoints=[] +ObsSW.Observation.DataProducts.Input_CoherentStokes.namemask= +ObsSW.Observation.DataProducts.Input_CoherentStokes.skip=[] +ObsSW.Observation.DataProducts.Input_Correlated.dirmask= +ObsSW.Observation.DataProducts.Input_Correlated.enabled=true +ObsSW.Observation.DataProducts.Input_Correlated.filenames=[L426528_SAP000_SB000_uv.MS,L426528_SAP000_SB001_uv.MS,L426528_SAP000_SB002_uv.MS,L426528_SAP000_SB003_uv.MS,L426528_SAP000_SB004_uv.MS,L426528_SAP000_SB005_uv.MS,L426528_SAP000_SB006_uv.MS,L426528_SAP000_SB007_uv.MS,L426528_SAP000_SB008_uv.MS,L426528_SAP000_SB009_uv.MS,L426528_SAP000_SB010_uv.MS,L426528_SAP000_SB011_uv.MS,L426528_SAP000_SB012_uv.MS,L426528_SAP000_SB013_uv.MS,L426528_SAP000_SB014_uv.MS,L426528_SAP000_SB015_uv.MS,L426528_SAP000_SB016_uv.MS,L426528_SAP000_SB017_uv.MS,L426528_SAP000_SB018_uv.MS,L426528_SAP000_SB019_uv.MS,L426528_SAP000_SB020_uv.MS,L426528_SAP000_SB021_uv.MS,L426528_SAP000_SB022_uv.MS,L426528_SAP000_SB023_uv.MS,L426528_SAP000_SB024_uv.MS,L426528_SAP000_SB025_uv.MS,L426528_SAP000_SB026_uv.MS,L426528_SAP000_SB027_uv.MS,L426528_SAP000_SB028_uv.MS,L426528_SAP000_SB029_uv.MS,L426528_SAP000_SB030_uv.MS,L426528_SAP000_SB031_uv.MS,L426528_SAP000_SB032_uv.MS,L426528_SAP000_SB033_uv.MS,L426528_SAP000_SB034_uv.MS,L426528_SAP000_SB035_uv.MS,L426528_SAP000_SB036_uv.MS,L426528_SAP000_SB037_uv.MS,L426528_SAP000_SB038_uv.MS,L426528_SAP000_SB039_uv.MS,L426528_SAP000_SB040_uv.MS,L426528_SAP000_SB041_uv.MS,L426528_SAP000_SB042_uv.MS,L426528_SAP000_SB043_uv.MS,L426528_SAP000_SB044_uv.MS,L426528_SAP000_SB045_uv.MS,L426528_SAP000_SB046_uv.MS,L426528_SAP000_SB047_uv.MS,L426528_SAP000_SB048_uv.MS,L426528_SAP000_SB049_uv.MS,L426528_SAP000_SB050_uv.MS,L426528_SAP000_SB051_uv.MS,L426528_SAP000_SB052_uv.MS,L426528_SAP000_SB053_uv.MS,L426528_SAP000_SB054_uv.MS,L426528_SAP000_SB055_uv.MS,L426528_SAP000_SB056_uv.MS,L426528_SAP000_SB057_uv.MS,L426528_SAP000_SB058_uv.MS,L426528_SAP000_SB059_uv.MS,L426528_SAP000_SB060_uv.MS,L426528_SAP000_SB061_uv.MS,L426528_SAP000_SB062_uv.MS,L426528_SAP000_SB063_uv.MS,L426528_SAP000_SB064_uv.MS,L426528_SAP000_SB065_uv.MS,L426528_SAP000_SB066_uv.MS,L426528_SAP000_SB067_uv.MS,L426528_SAP000_SB068_uv.MS,L426528_SAP000_SB069_uv.MS,L426528_SAP000_SB070_uv.MS,L426528_SAP000_SB071_uv.MS,L426528_SAP000_SB072_uv.MS,L426528_SAP000_SB073_uv.MS,L426528_SAP000_SB074_uv.MS,L426528_SAP000_SB075_uv.MS,L426528_SAP000_SB076_uv.MS,L426528_SAP000_SB077_uv.MS,L426528_SAP000_SB078_uv.MS,L426528_SAP000_SB079_uv.MS,L426528_SAP000_SB080_uv.MS,L426528_SAP000_SB081_uv.MS,L426528_SAP000_SB082_uv.MS,L426528_SAP000_SB083_uv.MS,L426528_SAP000_SB084_uv.MS,L426528_SAP000_SB085_uv.MS,L426528_SAP000_SB086_uv.MS,L426528_SAP000_SB087_uv.MS,L426528_SAP000_SB088_uv.MS,L426528_SAP000_SB089_uv.MS,L426528_SAP000_SB090_uv.MS,L426528_SAP000_SB091_uv.MS,L426528_SAP000_SB092_uv.MS,L426528_SAP000_SB093_uv.MS,L426528_SAP000_SB094_uv.MS,L426528_SAP000_SB095_uv.MS,L426528_SAP000_SB096_uv.MS,L426528_SAP000_SB097_uv.MS,L426528_SAP000_SB098_uv.MS,L426528_SAP000_SB099_uv.MS,L426528_SAP000_SB100_uv.MS,L426528_SAP000_SB101_uv.MS,L426528_SAP000_SB102_uv.MS,L426528_SAP000_SB103_uv.MS,L426528_SAP000_SB104_uv.MS,L426528_SAP000_SB105_uv.MS,L426528_SAP000_SB106_uv.MS,L426528_SAP000_SB107_uv.MS,L426528_SAP000_SB108_uv.MS,L426528_SAP000_SB109_uv.MS,L426528_SAP000_SB110_uv.MS,L426528_SAP000_SB111_uv.MS,L426528_SAP000_SB112_uv.MS,L426528_SAP000_SB113_uv.MS,L426528_SAP000_SB114_uv.MS,L426528_SAP000_SB115_uv.MS,L426528_SAP000_SB116_uv.MS,L426528_SAP000_SB117_uv.MS,L426528_SAP000_SB118_uv.MS,L426528_SAP000_SB119_uv.MS,L426528_SAP000_SB120_uv.MS,L426528_SAP000_SB121_uv.MS,L426528_SAP000_SB122_uv.MS,L426528_SAP000_SB123_uv.MS,L426528_SAP000_SB124_uv.MS,L426528_SAP000_SB125_uv.MS,L426528_SAP000_SB126_uv.MS,L426528_SAP000_SB127_uv.MS,L426528_SAP000_SB128_uv.MS,L426528_SAP000_SB129_uv.MS,L426528_SAP000_SB130_uv.MS,L426528_SAP000_SB131_uv.MS,L426528_SAP000_SB132_uv.MS,L426528_SAP000_SB133_uv.MS,L426528_SAP000_SB134_uv.MS,L426528_SAP000_SB135_uv.MS,L426528_SAP000_SB136_uv.MS,L426528_SAP000_SB137_uv.MS,L426528_SAP000_SB138_uv.MS,L426528_SAP000_SB139_uv.MS,L426528_SAP000_SB140_uv.MS,L426528_SAP000_SB141_uv.MS,L426528_SAP000_SB142_uv.MS,L426528_SAP000_SB143_uv.MS,L426528_SAP000_SB144_uv.MS,L426528_SAP000_SB145_uv.MS,L426528_SAP000_SB146_uv.MS,L426528_SAP000_SB147_uv.MS,L426528_SAP000_SB148_uv.MS,L426528_SAP000_SB149_uv.MS,L426528_SAP000_SB150_uv.MS,L426528_SAP000_SB151_uv.MS,L426528_SAP000_SB152_uv.MS,L426528_SAP000_SB153_uv.MS,L426528_SAP000_SB154_uv.MS,L426528_SAP000_SB155_uv.MS,L426528_SAP000_SB156_uv.MS,L426528_SAP000_SB157_uv.MS,L426528_SAP000_SB158_uv.MS,L426528_SAP000_SB159_uv.MS,L426528_SAP000_SB160_uv.MS,L426528_SAP000_SB161_uv.MS,L426528_SAP000_SB162_uv.MS,L426528_SAP000_SB163_uv.MS,L426528_SAP000_SB164_uv.MS,L426528_SAP000_SB165_uv.MS,L426528_SAP000_SB166_uv.MS,L426528_SAP000_SB167_uv.MS,L426528_SAP000_SB168_uv.MS,L426528_SAP000_SB169_uv.MS,L426528_SAP000_SB170_uv.MS,L426528_SAP000_SB171_uv.MS,L426528_SAP000_SB172_uv.MS,L426528_SAP000_SB173_uv.MS,L426528_SAP000_SB174_uv.MS,L426528_SAP000_SB175_uv.MS,L426528_SAP000_SB176_uv.MS,L426528_SAP000_SB177_uv.MS,L426528_SAP000_SB178_uv.MS,L426528_SAP000_SB179_uv.MS,L426528_SAP000_SB180_uv.MS,L426528_SAP000_SB181_uv.MS,L426528_SAP000_SB182_uv.MS,L426528_SAP000_SB183_uv.MS,L426528_SAP000_SB184_uv.MS,L426528_SAP000_SB185_uv.MS,L426528_SAP000_SB186_uv.MS,L426528_SAP000_SB187_uv.MS,L426528_SAP000_SB188_uv.MS,L426528_SAP000_SB189_uv.MS,L426528_SAP000_SB190_uv.MS,L426528_SAP000_SB191_uv.MS,L426528_SAP000_SB192_uv.MS,L426528_SAP000_SB193_uv.MS,L426528_SAP000_SB194_uv.MS,L426528_SAP000_SB195_uv.MS,L426528_SAP000_SB196_uv.MS,L426528_SAP000_SB197_uv.MS,L426528_SAP000_SB198_uv.MS,L426528_SAP000_SB199_uv.MS,L426528_SAP000_SB200_uv.MS,L426528_SAP000_SB201_uv.MS,L426528_SAP000_SB202_uv.MS,L426528_SAP000_SB203_uv.MS,L426528_SAP000_SB204_uv.MS,L426528_SAP000_SB205_uv.MS,L426528_SAP000_SB206_uv.MS,L426528_SAP000_SB207_uv.MS,L426528_SAP000_SB208_uv.MS,L426528_SAP000_SB209_uv.MS,L426528_SAP000_SB210_uv.MS,L426528_SAP000_SB211_uv.MS,L426528_SAP000_SB212_uv.MS,L426528_SAP000_SB213_uv.MS,L426528_SAP000_SB214_uv.MS,L426528_SAP000_SB215_uv.MS,L426528_SAP000_SB216_uv.MS,L426528_SAP000_SB217_uv.MS,L426528_SAP000_SB218_uv.MS,L426528_SAP000_SB219_uv.MS,L426528_SAP000_SB220_uv.MS,L426528_SAP000_SB221_uv.MS,L426528_SAP000_SB222_uv.MS,L426528_SAP000_SB223_uv.MS,L426528_SAP000_SB224_uv.MS,L426528_SAP000_SB225_uv.MS,L426528_SAP000_SB226_uv.MS,L426528_SAP000_SB227_uv.MS,L426528_SAP000_SB228_uv.MS,L426528_SAP000_SB229_uv.MS,L426528_SAP000_SB230_uv.MS,L426528_SAP000_SB231_uv.MS,L426528_SAP000_SB232_uv.MS,L426528_SAP000_SB233_uv.MS,L426528_SAP000_SB234_uv.MS,L426528_SAP000_SB235_uv.MS,L426528_SAP000_SB236_uv.MS,L426528_SAP000_SB237_uv.MS,L426528_SAP000_SB238_uv.MS,L426528_SAP000_SB239_uv.MS,L426528_SAP000_SB240_uv.MS,L426528_SAP000_SB241_uv.MS,L426528_SAP000_SB242_uv.MS,L426528_SAP000_SB243_uv.MS] +ObsSW.Observation.DataProducts.Input_Correlated.identifications=[mom.G630967.B0.1.T.SAP000.uv.dps] +ObsSW.Observation.DataProducts.Input_Correlated.locations=[locus001:/data/L426528/,locus003:/data/L426528/,locus004:/data/L426528/,locus005:/data/L426528/,locus006:/data/L426528/,locus007:/data/L426528/,locus008:/data/L426528/,locus009:/data/L426528/,locus010:/data/L426528/,locus011:/data/L426528/,locus012:/data/L426528/,locus014:/data/L426528/,locus016:/data/L426528/,locus017:/data/L426528/,locus018:/data/L426528/,locus020:/data/L426528/,locus021:/data/L426528/,locus023:/data/L426528/,locus025:/data/L426528/,locus026:/data/L426528/,locus027:/data/L426528/,locus028:/data/L426528/,locus029:/data/L426528/,locus031:/data/L426528/,locus032:/data/L426528/,locus034:/data/L426528/,locus035:/data/L426528/,locus036:/data/L426528/,locus037:/data/L426528/,locus038:/data/L426528/,locus039:/data/L426528/,locus040:/data/L426528/,locus041:/data/L426528/,locus042:/data/L426528/,locus043:/data/L426528/,locus044:/data/L426528/,locus045:/data/L426528/,locus046:/data/L426528/,locus047:/data/L426528/,locus048:/data/L426528/,locus049:/data/L426528/,locus050:/data/L426528/,locus051:/data/L426528/,locus052:/data/L426528/,locus053:/data/L426528/,locus054:/data/L426528/,locus055:/data/L426528/,locus056:/data/L426528/,locus057:/data/L426528/,locus058:/data/L426528/,locus060:/data/L426528/,locus061:/data/L426528/,locus062:/data/L426528/,locus063:/data/L426528/,locus064:/data/L426528/,locus065:/data/L426528/,locus066:/data/L426528/,locus067:/data/L426528/,locus068:/data/L426528/,locus069:/data/L426528/,locus070:/data/L426528/,locus071:/data/L426528/,locus072:/data/L426528/,locus073:/data/L426528/,locus074:/data/L426528/,locus075:/data/L426528/,locus076:/data/L426528/,locus077:/data/L426528/,locus078:/data/L426528/,locus079:/data/L426528/,locus080:/data/L426528/,locus081:/data/L426528/,locus082:/data/L426528/,locus084:/data/L426528/,locus085:/data/L426528/,locus086:/data/L426528/,locus087:/data/L426528/,locus088:/data/L426528/,locus089:/data/L426528/,locus090:/data/L426528/,locus091:/data/L426528/,locus001:/data/L426528/,locus003:/data/L426528/,locus004:/data/L426528/,locus005:/data/L426528/,locus006:/data/L426528/,locus007:/data/L426528/,locus008:/data/L426528/,locus009:/data/L426528/,locus010:/data/L426528/,locus011:/data/L426528/,locus012:/data/L426528/,locus014:/data/L426528/,locus016:/data/L426528/,locus017:/data/L426528/,locus018:/data/L426528/,locus020:/data/L426528/,locus021:/data/L426528/,locus023:/data/L426528/,locus025:/data/L426528/,locus026:/data/L426528/,locus027:/data/L426528/,locus028:/data/L426528/,locus029:/data/L426528/,locus031:/data/L426528/,locus032:/data/L426528/,locus034:/data/L426528/,locus035:/data/L426528/,locus036:/data/L426528/,locus037:/data/L426528/,locus038:/data/L426528/,locus039:/data/L426528/,locus040:/data/L426528/,locus041:/data/L426528/,locus042:/data/L426528/,locus043:/data/L426528/,locus044:/data/L426528/,locus045:/data/L426528/,locus046:/data/L426528/,locus047:/data/L426528/,locus048:/data/L426528/,locus049:/data/L426528/,locus050:/data/L426528/,locus051:/data/L426528/,locus052:/data/L426528/,locus053:/data/L426528/,locus054:/data/L426528/,locus055:/data/L426528/,locus056:/data/L426528/,locus057:/data/L426528/,locus058:/data/L426528/,locus060:/data/L426528/,locus061:/data/L426528/,locus062:/data/L426528/,locus063:/data/L426528/,locus064:/data/L426528/,locus065:/data/L426528/,locus066:/data/L426528/,locus067:/data/L426528/,locus068:/data/L426528/,locus069:/data/L426528/,locus070:/data/L426528/,locus071:/data/L426528/,locus072:/data/L426528/,locus073:/data/L426528/,locus074:/data/L426528/,locus075:/data/L426528/,locus076:/data/L426528/,locus077:/data/L426528/,locus078:/data/L426528/,locus079:/data/L426528/,locus080:/data/L426528/,locus081:/data/L426528/,locus082:/data/L426528/,locus084:/data/L426528/,locus085:/data/L426528/,locus086:/data/L426528/,locus087:/data/L426528/,locus088:/data/L426528/,locus089:/data/L426528/,locus090:/data/L426528/,locus091:/data/L426528/,locus001:/data/L426528/,locus003:/data/L426528/,locus004:/data/L426528/,locus005:/data/L426528/,locus006:/data/L426528/,locus007:/data/L426528/,locus008:/data/L426528/,locus009:/data/L426528/,locus010:/data/L426528/,locus011:/data/L426528/,locus012:/data/L426528/,locus014:/data/L426528/,locus016:/data/L426528/,locus017:/data/L426528/,locus018:/data/L426528/,locus020:/data/L426528/,locus021:/data/L426528/,locus023:/data/L426528/,locus025:/data/L426528/,locus026:/data/L426528/,locus027:/data/L426528/,locus028:/data/L426528/,locus029:/data/L426528/,locus031:/data/L426528/,locus032:/data/L426528/,locus034:/data/L426528/,locus035:/data/L426528/,locus036:/data/L426528/,locus037:/data/L426528/,locus038:/data/L426528/,locus039:/data/L426528/,locus040:/data/L426528/,locus041:/data/L426528/,locus042:/data/L426528/,locus043:/data/L426528/,locus044:/data/L426528/,locus045:/data/L426528/,locus046:/data/L426528/,locus047:/data/L426528/,locus048:/data/L426528/,locus049:/data/L426528/,locus050:/data/L426528/,locus051:/data/L426528/,locus052:/data/L426528/,locus053:/data/L426528/,locus054:/data/L426528/,locus055:/data/L426528/,locus056:/data/L426528/,locus057:/data/L426528/,locus058:/data/L426528/,locus060:/data/L426528/,locus061:/data/L426528/,locus062:/data/L426528/,locus063:/data/L426528/,locus064:/data/L426528/,locus065:/data/L426528/,locus066:/data/L426528/,locus067:/data/L426528/,locus068:/data/L426528/,locus069:/data/L426528/,locus070:/data/L426528/,locus071:/data/L426528/,locus072:/data/L426528/,locus073:/data/L426528/,locus074:/data/L426528/,locus075:/data/L426528/,locus076:/data/L426528/,locus077:/data/L426528/,locus078:/data/L426528/,locus079:/data/L426528/,locus080:/data/L426528/,locus081:/data/L426528/,locus082:/data/L426528/,locus084:/data/L426528/,locus085:/data/L426528/,locus086:/data/L426528/,locus087:/data/L426528/,locus088:/data/L426528/,locus089:/data/L426528/,locus090:/data/L426528/,locus091:/data/L426528/,locus001:/data/L426528/] +ObsSW.Observation.DataProducts.Input_Correlated.mountpoints=[] +ObsSW.Observation.DataProducts.Input_Correlated.namemask= +ObsSW.Observation.DataProducts.Input_Correlated.skip=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +ObsSW.Observation.DataProducts.Input_IncoherentStokes.dirmask= +ObsSW.Observation.DataProducts.Input_IncoherentStokes.enabled=false +ObsSW.Observation.DataProducts.Input_IncoherentStokes.filenames=[] +ObsSW.Observation.DataProducts.Input_IncoherentStokes.identifications=[] +ObsSW.Observation.DataProducts.Input_IncoherentStokes.locations=[] +ObsSW.Observation.DataProducts.Input_IncoherentStokes.mountpoints=[] +ObsSW.Observation.DataProducts.Input_IncoherentStokes.namemask= +ObsSW.Observation.DataProducts.Input_IncoherentStokes.skip=[] +ObsSW.Observation.DataProducts.Input_InstrumentModel.dirmask= +ObsSW.Observation.DataProducts.Input_InstrumentModel.enabled=false +ObsSW.Observation.DataProducts.Input_InstrumentModel.filenames=[] +ObsSW.Observation.DataProducts.Input_InstrumentModel.identifications=[] +ObsSW.Observation.DataProducts.Input_InstrumentModel.locations=[] +ObsSW.Observation.DataProducts.Input_InstrumentModel.mountpoints=[] +ObsSW.Observation.DataProducts.Input_InstrumentModel.namemask= +ObsSW.Observation.DataProducts.Input_InstrumentModel.skip=[] +ObsSW.Observation.DataProducts.Input_SkyImage.dirmask= +ObsSW.Observation.DataProducts.Input_SkyImage.enabled=false +ObsSW.Observation.DataProducts.Input_SkyImage.filenames=[] +ObsSW.Observation.DataProducts.Input_SkyImage.identifications=[] +ObsSW.Observation.DataProducts.Input_SkyImage.locations=[] +ObsSW.Observation.DataProducts.Input_SkyImage.mountpoints=[] +ObsSW.Observation.DataProducts.Input_SkyImage.namemask= +ObsSW.Observation.DataProducts.Input_SkyImage.skip=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.archived=false +ObsSW.Observation.DataProducts.Output_CoherentStokes.deleted=false +ObsSW.Observation.DataProducts.Output_CoherentStokes.dirmask= +ObsSW.Observation.DataProducts.Output_CoherentStokes.enabled=false +ObsSW.Observation.DataProducts.Output_CoherentStokes.filenames=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.identifications=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.locations=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.mountpoints=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.namemask= +ObsSW.Observation.DataProducts.Output_CoherentStokes.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_CoherentStokes.retentiontime=14 +ObsSW.Observation.DataProducts.Output_CoherentStokes.skip=[] +ObsSW.Observation.DataProducts.Output_Correlated.archived=false +ObsSW.Observation.DataProducts.Output_Correlated.deleted=false +ObsSW.Observation.DataProducts.Output_Correlated.dirmask=L${OBSID} +ObsSW.Observation.DataProducts.Output_Correlated.enabled=true +ObsSW.Observation.DataProducts.Output_Correlated.filenames=[L426942_SB000_uv.dppp.MS,L426942_SB001_uv.dppp.MS,L426942_SB002_uv.dppp.MS,L426942_SB003_uv.dppp.MS,L426942_SB004_uv.dppp.MS,L426942_SB005_uv.dppp.MS,L426942_SB006_uv.dppp.MS,L426942_SB007_uv.dppp.MS,L426942_SB008_uv.dppp.MS,L426942_SB009_uv.dppp.MS,L426942_SB010_uv.dppp.MS,L426942_SB011_uv.dppp.MS,L426942_SB012_uv.dppp.MS,L426942_SB013_uv.dppp.MS,L426942_SB014_uv.dppp.MS,L426942_SB015_uv.dppp.MS,L426942_SB016_uv.dppp.MS,L426942_SB017_uv.dppp.MS,L426942_SB018_uv.dppp.MS,L426942_SB019_uv.dppp.MS,L426942_SB020_uv.dppp.MS,L426942_SB021_uv.dppp.MS,L426942_SB022_uv.dppp.MS,L426942_SB023_uv.dppp.MS,L426942_SB024_uv.dppp.MS,L426942_SB025_uv.dppp.MS,L426942_SB026_uv.dppp.MS,L426942_SB027_uv.dppp.MS,L426942_SB028_uv.dppp.MS,L426942_SB029_uv.dppp.MS,L426942_SB030_uv.dppp.MS,L426942_SB031_uv.dppp.MS,L426942_SB032_uv.dppp.MS,L426942_SB033_uv.dppp.MS,L426942_SB034_uv.dppp.MS,L426942_SB035_uv.dppp.MS,L426942_SB036_uv.dppp.MS,L426942_SB037_uv.dppp.MS,L426942_SB038_uv.dppp.MS,L426942_SB039_uv.dppp.MS,L426942_SB040_uv.dppp.MS,L426942_SB041_uv.dppp.MS,L426942_SB042_uv.dppp.MS,L426942_SB043_uv.dppp.MS,L426942_SB044_uv.dppp.MS,L426942_SB045_uv.dppp.MS,L426942_SB046_uv.dppp.MS,L426942_SB047_uv.dppp.MS,L426942_SB048_uv.dppp.MS,L426942_SB049_uv.dppp.MS,L426942_SB050_uv.dppp.MS,L426942_SB051_uv.dppp.MS,L426942_SB052_uv.dppp.MS,L426942_SB053_uv.dppp.MS,L426942_SB054_uv.dppp.MS,L426942_SB055_uv.dppp.MS,L426942_SB056_uv.dppp.MS,L426942_SB057_uv.dppp.MS,L426942_SB058_uv.dppp.MS,L426942_SB059_uv.dppp.MS,L426942_SB060_uv.dppp.MS,L426942_SB061_uv.dppp.MS,L426942_SB062_uv.dppp.MS,L426942_SB063_uv.dppp.MS,L426942_SB064_uv.dppp.MS,L426942_SB065_uv.dppp.MS,L426942_SB066_uv.dppp.MS,L426942_SB067_uv.dppp.MS,L426942_SB068_uv.dppp.MS,L426942_SB069_uv.dppp.MS,L426942_SB070_uv.dppp.MS,L426942_SB071_uv.dppp.MS,L426942_SB072_uv.dppp.MS,L426942_SB073_uv.dppp.MS,L426942_SB074_uv.dppp.MS,L426942_SB075_uv.dppp.MS,L426942_SB076_uv.dppp.MS,L426942_SB077_uv.dppp.MS,L426942_SB078_uv.dppp.MS,L426942_SB079_uv.dppp.MS,L426942_SB080_uv.dppp.MS,L426942_SB081_uv.dppp.MS,L426942_SB082_uv.dppp.MS,L426942_SB083_uv.dppp.MS,L426942_SB084_uv.dppp.MS,L426942_SB085_uv.dppp.MS,L426942_SB086_uv.dppp.MS,L426942_SB087_uv.dppp.MS,L426942_SB088_uv.dppp.MS,L426942_SB089_uv.dppp.MS,L426942_SB090_uv.dppp.MS,L426942_SB091_uv.dppp.MS,L426942_SB092_uv.dppp.MS,L426942_SB093_uv.dppp.MS,L426942_SB094_uv.dppp.MS,L426942_SB095_uv.dppp.MS,L426942_SB096_uv.dppp.MS,L426942_SB097_uv.dppp.MS,L426942_SB098_uv.dppp.MS,L426942_SB099_uv.dppp.MS,L426942_SB100_uv.dppp.MS,L426942_SB101_uv.dppp.MS,L426942_SB102_uv.dppp.MS,L426942_SB103_uv.dppp.MS,L426942_SB104_uv.dppp.MS,L426942_SB105_uv.dppp.MS,L426942_SB106_uv.dppp.MS,L426942_SB107_uv.dppp.MS,L426942_SB108_uv.dppp.MS,L426942_SB109_uv.dppp.MS,L426942_SB110_uv.dppp.MS,L426942_SB111_uv.dppp.MS,L426942_SB112_uv.dppp.MS,L426942_SB113_uv.dppp.MS,L426942_SB114_uv.dppp.MS,L426942_SB115_uv.dppp.MS,L426942_SB116_uv.dppp.MS,L426942_SB117_uv.dppp.MS,L426942_SB118_uv.dppp.MS,L426942_SB119_uv.dppp.MS,L426942_SB120_uv.dppp.MS,L426942_SB121_uv.dppp.MS,L426942_SB122_uv.dppp.MS,L426942_SB123_uv.dppp.MS,L426942_SB124_uv.dppp.MS,L426942_SB125_uv.dppp.MS,L426942_SB126_uv.dppp.MS,L426942_SB127_uv.dppp.MS,L426942_SB128_uv.dppp.MS,L426942_SB129_uv.dppp.MS,L426942_SB130_uv.dppp.MS,L426942_SB131_uv.dppp.MS,L426942_SB132_uv.dppp.MS,L426942_SB133_uv.dppp.MS,L426942_SB134_uv.dppp.MS,L426942_SB135_uv.dppp.MS,L426942_SB136_uv.dppp.MS,L426942_SB137_uv.dppp.MS,L426942_SB138_uv.dppp.MS,L426942_SB139_uv.dppp.MS,L426942_SB140_uv.dppp.MS,L426942_SB141_uv.dppp.MS,L426942_SB142_uv.dppp.MS,L426942_SB143_uv.dppp.MS,L426942_SB144_uv.dppp.MS,L426942_SB145_uv.dppp.MS,L426942_SB146_uv.dppp.MS,L426942_SB147_uv.dppp.MS,L426942_SB148_uv.dppp.MS,L426942_SB149_uv.dppp.MS,L426942_SB150_uv.dppp.MS,L426942_SB151_uv.dppp.MS,L426942_SB152_uv.dppp.MS,L426942_SB153_uv.dppp.MS,L426942_SB154_uv.dppp.MS,L426942_SB155_uv.dppp.MS,L426942_SB156_uv.dppp.MS,L426942_SB157_uv.dppp.MS,L426942_SB158_uv.dppp.MS,L426942_SB159_uv.dppp.MS,L426942_SB160_uv.dppp.MS,L426942_SB161_uv.dppp.MS,L426942_SB162_uv.dppp.MS,L426942_SB163_uv.dppp.MS,L426942_SB164_uv.dppp.MS,L426942_SB165_uv.dppp.MS,L426942_SB166_uv.dppp.MS,L426942_SB167_uv.dppp.MS,L426942_SB168_uv.dppp.MS,L426942_SB169_uv.dppp.MS,L426942_SB170_uv.dppp.MS,L426942_SB171_uv.dppp.MS,L426942_SB172_uv.dppp.MS,L426942_SB173_uv.dppp.MS,L426942_SB174_uv.dppp.MS,L426942_SB175_uv.dppp.MS,L426942_SB176_uv.dppp.MS,L426942_SB177_uv.dppp.MS,L426942_SB178_uv.dppp.MS,L426942_SB179_uv.dppp.MS,L426942_SB180_uv.dppp.MS,L426942_SB181_uv.dppp.MS,L426942_SB182_uv.dppp.MS,L426942_SB183_uv.dppp.MS,L426942_SB184_uv.dppp.MS,L426942_SB185_uv.dppp.MS,L426942_SB186_uv.dppp.MS,L426942_SB187_uv.dppp.MS,L426942_SB188_uv.dppp.MS,L426942_SB189_uv.dppp.MS,L426942_SB190_uv.dppp.MS,L426942_SB191_uv.dppp.MS,L426942_SB192_uv.dppp.MS,L426942_SB193_uv.dppp.MS,L426942_SB194_uv.dppp.MS,L426942_SB195_uv.dppp.MS,L426942_SB196_uv.dppp.MS,L426942_SB197_uv.dppp.MS,L426942_SB198_uv.dppp.MS,L426942_SB199_uv.dppp.MS,L426942_SB200_uv.dppp.MS,L426942_SB201_uv.dppp.MS,L426942_SB202_uv.dppp.MS,L426942_SB203_uv.dppp.MS,L426942_SB204_uv.dppp.MS,L426942_SB205_uv.dppp.MS,L426942_SB206_uv.dppp.MS,L426942_SB207_uv.dppp.MS,L426942_SB208_uv.dppp.MS,L426942_SB209_uv.dppp.MS,L426942_SB210_uv.dppp.MS,L426942_SB211_uv.dppp.MS,L426942_SB212_uv.dppp.MS,L426942_SB213_uv.dppp.MS,L426942_SB214_uv.dppp.MS,L426942_SB215_uv.dppp.MS,L426942_SB216_uv.dppp.MS,L426942_SB217_uv.dppp.MS,L426942_SB218_uv.dppp.MS,L426942_SB219_uv.dppp.MS,L426942_SB220_uv.dppp.MS,L426942_SB221_uv.dppp.MS,L426942_SB222_uv.dppp.MS,L426942_SB223_uv.dppp.MS,L426942_SB224_uv.dppp.MS,L426942_SB225_uv.dppp.MS,L426942_SB226_uv.dppp.MS,L426942_SB227_uv.dppp.MS,L426942_SB228_uv.dppp.MS,L426942_SB229_uv.dppp.MS,L426942_SB230_uv.dppp.MS,L426942_SB231_uv.dppp.MS,L426942_SB232_uv.dppp.MS,L426942_SB233_uv.dppp.MS,L426942_SB234_uv.dppp.MS,L426942_SB235_uv.dppp.MS,L426942_SB236_uv.dppp.MS,L426942_SB237_uv.dppp.MS,L426942_SB238_uv.dppp.MS,L426942_SB239_uv.dppp.MS,L426942_SB240_uv.dppp.MS,L426942_SB241_uv.dppp.MS,L426942_SB242_uv.dppp.MS,L426942_SB243_uv.dppp.MS] +ObsSW.Observation.DataProducts.Output_Correlated.identifications=[mom.G630967.M631401.fP.dps.uv] +ObsSW.Observation.DataProducts.Output_Correlated.locations=[locus001:/data/L426942/,locus003:/data/L426942/,locus004:/data/L426942/,locus005:/data/L426942/,locus006:/data/L426942/,locus007:/data/L426942/,locus008:/data/L426942/,locus009:/data/L426942/,locus010:/data/L426942/,locus011:/data/L426942/,locus012:/data/L426942/,locus014:/data/L426942/,locus016:/data/L426942/,locus017:/data/L426942/,locus018:/data/L426942/,locus020:/data/L426942/,locus021:/data/L426942/,locus023:/data/L426942/,locus025:/data/L426942/,locus026:/data/L426942/,locus027:/data/L426942/,locus028:/data/L426942/,locus029:/data/L426942/,locus031:/data/L426942/,locus032:/data/L426942/,locus034:/data/L426942/,locus035:/data/L426942/,locus036:/data/L426942/,locus037:/data/L426942/,locus038:/data/L426942/,locus039:/data/L426942/,locus040:/data/L426942/,locus041:/data/L426942/,locus042:/data/L426942/,locus043:/data/L426942/,locus044:/data/L426942/,locus045:/data/L426942/,locus046:/data/L426942/,locus047:/data/L426942/,locus048:/data/L426942/,locus049:/data/L426942/,locus050:/data/L426942/,locus051:/data/L426942/,locus052:/data/L426942/,locus053:/data/L426942/,locus054:/data/L426942/,locus055:/data/L426942/,locus056:/data/L426942/,locus057:/data/L426942/,locus058:/data/L426942/,locus060:/data/L426942/,locus061:/data/L426942/,locus062:/data/L426942/,locus063:/data/L426942/,locus064:/data/L426942/,locus065:/data/L426942/,locus066:/data/L426942/,locus067:/data/L426942/,locus068:/data/L426942/,locus069:/data/L426942/,locus070:/data/L426942/,locus071:/data/L426942/,locus072:/data/L426942/,locus073:/data/L426942/,locus074:/data/L426942/,locus075:/data/L426942/,locus076:/data/L426942/,locus077:/data/L426942/,locus078:/data/L426942/,locus079:/data/L426942/,locus080:/data/L426942/,locus081:/data/L426942/,locus082:/data/L426942/,locus084:/data/L426942/,locus085:/data/L426942/,locus086:/data/L426942/,locus087:/data/L426942/,locus088:/data/L426942/,locus089:/data/L426942/,locus090:/data/L426942/,locus091:/data/L426942/,locus001:/data/L426942/,locus003:/data/L426942/,locus004:/data/L426942/,locus005:/data/L426942/,locus006:/data/L426942/,locus007:/data/L426942/,locus008:/data/L426942/,locus009:/data/L426942/,locus010:/data/L426942/,locus011:/data/L426942/,locus012:/data/L426942/,locus014:/data/L426942/,locus016:/data/L426942/,locus017:/data/L426942/,locus018:/data/L426942/,locus020:/data/L426942/,locus021:/data/L426942/,locus023:/data/L426942/,locus025:/data/L426942/,locus026:/data/L426942/,locus027:/data/L426942/,locus028:/data/L426942/,locus029:/data/L426942/,locus031:/data/L426942/,locus032:/data/L426942/,locus034:/data/L426942/,locus035:/data/L426942/,locus036:/data/L426942/,locus037:/data/L426942/,locus038:/data/L426942/,locus039:/data/L426942/,locus040:/data/L426942/,locus041:/data/L426942/,locus042:/data/L426942/,locus043:/data/L426942/,locus044:/data/L426942/,locus045:/data/L426942/,locus046:/data/L426942/,locus047:/data/L426942/,locus048:/data/L426942/,locus049:/data/L426942/,locus050:/data/L426942/,locus051:/data/L426942/,locus052:/data/L426942/,locus053:/data/L426942/,locus054:/data/L426942/,locus055:/data/L426942/,locus056:/data/L426942/,locus057:/data/L426942/,locus058:/data/L426942/,locus060:/data/L426942/,locus061:/data/L426942/,locus062:/data/L426942/,locus063:/data/L426942/,locus064:/data/L426942/,locus065:/data/L426942/,locus066:/data/L426942/,locus067:/data/L426942/,locus068:/data/L426942/,locus069:/data/L426942/,locus070:/data/L426942/,locus071:/data/L426942/,locus072:/data/L426942/,locus073:/data/L426942/,locus074:/data/L426942/,locus075:/data/L426942/,locus076:/data/L426942/,locus077:/data/L426942/,locus078:/data/L426942/,locus079:/data/L426942/,locus080:/data/L426942/,locus081:/data/L426942/,locus082:/data/L426942/,locus084:/data/L426942/,locus085:/data/L426942/,locus086:/data/L426942/,locus087:/data/L426942/,locus088:/data/L426942/,locus089:/data/L426942/,locus090:/data/L426942/,locus091:/data/L426942/,locus001:/data/L426942/,locus003:/data/L426942/,locus004:/data/L426942/,locus005:/data/L426942/,locus006:/data/L426942/,locus007:/data/L426942/,locus008:/data/L426942/,locus009:/data/L426942/,locus010:/data/L426942/,locus011:/data/L426942/,locus012:/data/L426942/,locus014:/data/L426942/,locus016:/data/L426942/,locus017:/data/L426942/,locus018:/data/L426942/,locus020:/data/L426942/,locus021:/data/L426942/,locus023:/data/L426942/,locus025:/data/L426942/,locus026:/data/L426942/,locus027:/data/L426942/,locus028:/data/L426942/,locus029:/data/L426942/,locus031:/data/L426942/,locus032:/data/L426942/,locus034:/data/L426942/,locus035:/data/L426942/,locus036:/data/L426942/,locus037:/data/L426942/,locus038:/data/L426942/,locus039:/data/L426942/,locus040:/data/L426942/,locus041:/data/L426942/,locus042:/data/L426942/,locus043:/data/L426942/,locus044:/data/L426942/,locus045:/data/L426942/,locus046:/data/L426942/,locus047:/data/L426942/,locus048:/data/L426942/,locus049:/data/L426942/,locus050:/data/L426942/,locus051:/data/L426942/,locus052:/data/L426942/,locus053:/data/L426942/,locus054:/data/L426942/,locus055:/data/L426942/,locus056:/data/L426942/,locus057:/data/L426942/,locus058:/data/L426942/,locus060:/data/L426942/,locus061:/data/L426942/,locus062:/data/L426942/,locus063:/data/L426942/,locus064:/data/L426942/,locus065:/data/L426942/,locus066:/data/L426942/,locus067:/data/L426942/,locus068:/data/L426942/,locus069:/data/L426942/,locus070:/data/L426942/,locus071:/data/L426942/,locus072:/data/L426942/,locus073:/data/L426942/,locus074:/data/L426942/,locus075:/data/L426942/,locus076:/data/L426942/,locus077:/data/L426942/,locus078:/data/L426942/,locus079:/data/L426942/,locus080:/data/L426942/,locus081:/data/L426942/,locus082:/data/L426942/,locus084:/data/L426942/,locus085:/data/L426942/,locus086:/data/L426942/,locus087:/data/L426942/,locus088:/data/L426942/,locus089:/data/L426942/,locus090:/data/L426942/,locus091:/data/L426942/,locus001:/data/L426942/] +ObsSW.Observation.DataProducts.Output_Correlated.mountpoints=[locus001:/data,locus003:/data,locus004:/data,locus005:/data,locus006:/data,locus007:/data,locus008:/data,locus009:/data,locus010:/data,locus011:/data,locus012:/data,locus014:/data,locus016:/data,locus017:/data,locus018:/data,locus020:/data,locus021:/data,locus023:/data,locus025:/data,locus026:/data,locus027:/data,locus028:/data,locus029:/data,locus031:/data,locus032:/data,locus034:/data,locus035:/data,locus036:/data,locus037:/data,locus038:/data,locus039:/data,locus040:/data,locus041:/data,locus042:/data,locus043:/data,locus044:/data,locus045:/data,locus046:/data,locus047:/data,locus048:/data,locus049:/data,locus050:/data,locus051:/data,locus052:/data,locus053:/data,locus054:/data,locus055:/data,locus056:/data,locus057:/data,locus058:/data,locus060:/data,locus061:/data,locus062:/data,locus063:/data,locus064:/data,locus065:/data,locus066:/data,locus067:/data,locus068:/data,locus069:/data,locus070:/data,locus071:/data,locus072:/data,locus073:/data,locus074:/data,locus075:/data,locus076:/data,locus077:/data,locus078:/data,locus079:/data,locus080:/data,locus081:/data,locus082:/data,locus084:/data,locus085:/data,locus086:/data,locus087:/data,locus088:/data,locus089:/data,locus090:/data,locus091:/data] +ObsSW.Observation.DataProducts.Output_Correlated.namemask=L${OBSID}_SAP${SAP}_SB${SUBBAND}_uv.MS +ObsSW.Observation.DataProducts.Output_Correlated.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_Correlated.retentiontime=14 +ObsSW.Observation.DataProducts.Output_Correlated.skip=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.archived=false +ObsSW.Observation.DataProducts.Output_IncoherentStokes.deleted=false +ObsSW.Observation.DataProducts.Output_IncoherentStokes.dirmask= +ObsSW.Observation.DataProducts.Output_IncoherentStokes.enabled=false +ObsSW.Observation.DataProducts.Output_IncoherentStokes.filenames=[] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.identifications=[] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.locations=[] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.mountpoints=[] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.namemask= +ObsSW.Observation.DataProducts.Output_IncoherentStokes.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_IncoherentStokes.retentiontime=14 +ObsSW.Observation.DataProducts.Output_IncoherentStokes.skip=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.archived=false +ObsSW.Observation.DataProducts.Output_InstrumentModel.deleted=false +ObsSW.Observation.DataProducts.Output_InstrumentModel.dirmask= +ObsSW.Observation.DataProducts.Output_InstrumentModel.enabled=false +ObsSW.Observation.DataProducts.Output_InstrumentModel.filenames=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.identifications=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.locations=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.mountpoints=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.namemask= +ObsSW.Observation.DataProducts.Output_InstrumentModel.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_InstrumentModel.retentiontime=14 +ObsSW.Observation.DataProducts.Output_InstrumentModel.skip=[] +ObsSW.Observation.DataProducts.Output_Pulsar.archived=false +ObsSW.Observation.DataProducts.Output_Pulsar.deleted=false +ObsSW.Observation.DataProducts.Output_Pulsar.dirmask= +ObsSW.Observation.DataProducts.Output_Pulsar.enabled=false +ObsSW.Observation.DataProducts.Output_Pulsar.filenames=[] +ObsSW.Observation.DataProducts.Output_Pulsar.identifications=[] +ObsSW.Observation.DataProducts.Output_Pulsar.locations=[] +ObsSW.Observation.DataProducts.Output_Pulsar.mountpoints=[] +ObsSW.Observation.DataProducts.Output_Pulsar.namemask= +ObsSW.Observation.DataProducts.Output_Pulsar.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_Pulsar.retentiontime=14 +ObsSW.Observation.DataProducts.Output_Pulsar.skip=[] +ObsSW.Observation.DataProducts.Output_SkyImage.archived=false +ObsSW.Observation.DataProducts.Output_SkyImage.deleted=false +ObsSW.Observation.DataProducts.Output_SkyImage.dirmask= +ObsSW.Observation.DataProducts.Output_SkyImage.enabled=false +ObsSW.Observation.DataProducts.Output_SkyImage.filenames=[] +ObsSW.Observation.DataProducts.Output_SkyImage.identifications=[] +ObsSW.Observation.DataProducts.Output_SkyImage.locations=[] +ObsSW.Observation.DataProducts.Output_SkyImage.mountpoints=[] +ObsSW.Observation.DataProducts.Output_SkyImage.namemask= +ObsSW.Observation.DataProducts.Output_SkyImage.percentageWritten=[] +ObsSW.Observation.DataProducts.Output_SkyImage.retentiontime=14 +ObsSW.Observation.DataProducts.Output_SkyImage.skip=[] +ObsSW.Observation.ObservationControl.PythonControl.Calibration.SkyModel= +ObsSW.Observation.ObservationControl.PythonControl.Calibration.exportCalibrationParameters=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.autocorr=F +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.count.path=- +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.count.save=FALSE +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.keepstatistics=T +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.memorymax=10 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.memoryperc=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.overlapmax=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.overlapperc=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.pedantic=F +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.pulsar=F +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.strategy=HBAdefault +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.timewindow=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.aoflagger.type=aoflagger +ObsSW.Observation.ObservationControl.PythonControl.DPPP.checkparset=-1 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.baseline=CS*,RS*& +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.blrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.corrtype=cross +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.demixfreqstep=64 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.demixtimestep=10 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.elevationcutoff=0.0deg +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.freqstep=4 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.ignoretarget=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.instrumentmodel=instrument +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.modelsources=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.ntimechunk=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.othersources=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.skymodel=sky +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.subtractsources= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.targetsource= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.timestep=1 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.demixer.type=demixer +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.autoweight=true +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.band=-1 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.baseline= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.blrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.corrtype= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.datacolumn=DATA +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.forceautoweight=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.missingdata=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.nchan=nchan +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.orderms=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.sort=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.startchan=0 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msin.useflag=true +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msout.overwrite=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msout.tilenchan=8 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msout.tilesize=1024 +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msout.vdsdir=A +ObsSW.Observation.ObservationControl.PythonControl.DPPP.msout.writefullresflag=true +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].abstime=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].azimuth=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].baseline= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].blrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].chan=[0..nchan/32-1,31*nchan/32..nchan-1] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].corrtype= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].count.path=- +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].count.save=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].elevation=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].expr= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].freqrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].lst=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].reltime=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].timeofday=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].timeslot=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[0].type=preflagger +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].abstime=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].azimuth=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].baseline= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].blrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].chan=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].corrtype=auto +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].count.path=- +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].count.save=false +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].elevation=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].expr= +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].freqrange=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].lst=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].reltime=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].timeofday=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].timeslot=[] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.preflagger[1].type=preflagger +ObsSW.Observation.ObservationControl.PythonControl.DPPP.showprogress=F +ObsSW.Observation.ObservationControl.PythonControl.DPPP.showtimings=F +ObsSW.Observation.ObservationControl.PythonControl.DPPP.steps=[preflagger[0],preflagger[1],aoflagger,demixer] +ObsSW.Observation.ObservationControl.PythonControl.DPPP.uselogger=T +ObsSW.Observation.ObservationControl.PythonControl.LongBaseline.subbandgroups_per_ms=1 +ObsSW.Observation.ObservationControl.PythonControl.LongBaseline.subbands_per_subbandgroup=1 +ObsSW.Observation.ObservationControl.PythonControl.PreProcessing.SkyModel=Ateam_LBA_CC +ObsSW.Observation.ObservationControl.PythonControl.PreProcessing.demix_always=[] +ObsSW.Observation.ObservationControl.PythonControl.PreProcessing.demix_if_needed=[] +ObsSW.Observation.ObservationControl.PythonControl._hostname=CCU001 +ObsSW.Observation.ObservationControl.PythonControl.canCommunicate=false +ObsSW.Observation.ObservationControl.PythonControl.pythonHost=lhn001.cep2.lofar +ObsSW.Observation.ObservationControl.PythonControl.pythonProgram=preprocessing_pipeline.py +ObsSW.Observation.ObservationControl.PythonControl.softwareVersion= +ObsSW.Observation.ObservationControl._hostname=MCU001 +ObsSW.Observation.ObservationControl.heartbeatInterval=10 +ObsSW.Observation.Scheduler.contactEmail= +ObsSW.Observation.Scheduler.contactName= +ObsSW.Observation.Scheduler.contactPhone= +ObsSW.Observation.Scheduler.firstPossibleDay=0 +ObsSW.Observation.Scheduler.fixedDay=false +ObsSW.Observation.Scheduler.fixedTime=false +ObsSW.Observation.Scheduler.lastPossibleDay=0 +ObsSW.Observation.Scheduler.late=false +ObsSW.Observation.Scheduler.nightTimeWeightFactor=0 +ObsSW.Observation.Scheduler.predMaxTimeDif= +ObsSW.Observation.Scheduler.predMinTimeDif= +ObsSW.Observation.Scheduler.predecessors=[L426528] +ObsSW.Observation.Scheduler.priority=0.0 +ObsSW.Observation.Scheduler.reason= +ObsSW.Observation.Scheduler.referenceFrame=0 +ObsSW.Observation.Scheduler.reservation=0 +ObsSW.Observation.Scheduler.storageSelectionMode=1 +ObsSW.Observation.Scheduler.taskDuration=600 +ObsSW.Observation.Scheduler.taskID=1450 +ObsSW.Observation.Scheduler.taskName=Preprocessing_newHBA +ObsSW.Observation.Scheduler.taskType=0 +ObsSW.Observation.Scheduler.windowMaximumTime= +ObsSW.Observation.Scheduler.windowMinimumTime= +ObsSW.Observation.VirtualInstrument.minimalNrStations=1 +ObsSW.Observation.VirtualInstrument.stationList=[] +ObsSW.Observation.VirtualInstrument.stationSet= +ObsSW.Observation.antennaArray=LBA +ObsSW.Observation.antennaSet=LBA_INNER +ObsSW.Observation.bandFilter=LBA_30_90 +ObsSW.Observation.claimPeriod=10 +ObsSW.Observation.clockMode=<<Clock200 +ObsSW.Observation.momID=631401 +ObsSW.Observation.nrAnaBeams=0 +ObsSW.Observation.nrBeams=0 +ObsSW.Observation.nrBitsPerSample=16 +ObsSW.Observation.nrTBBSettings=0 +ObsSW.Observation.originID=254009 +ObsSW.Observation.otdbID=426942 +ObsSW.Observation.preparePeriod=10 +ObsSW.Observation.processSubtype=Averaging Pipeline +ObsSW.Observation.processType=Pipeline +ObsSW.Observation.sampleClock=200 +ObsSW.Observation.startTime=2016-01-19 11:50:00 +ObsSW.Observation.stopTime=2016-01-19 12:00:00 +ObsSW.Observation.strategy=Preprocessing only +ObsSW.Observation.topologyID=mom.G630967.M631401.fP +Version.number=30577 +_DPname=LOFAR_ObsSW_TempObs0162 +prefix=LOFAR. diff --git a/SAS/ResourceAssignment/Services/test/tRATaskSpecified.py b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.py new file mode 100644 index 0000000000000000000000000000000000000000..f3b29ef117da187e6ed5f3001d1b829ed4dc2f08 --- /dev/null +++ b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +# Be able to find service python file +import sys, os +sys.path.insert(0, "{srcdir}/../src".format(**os.environ)) + +from RATaskSpecified import * +from RABusListener import RATaskSpecifiedBusListener +from lofar.parameterset import PyParameterSet +from lofar.messaging import EventMessage, Service +from lofar.common.methodtrigger import MethodTrigger + +import unittest +from glob import glob +import uuid +import datetime + +import logging +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + +def setUpModule(): + pass + +def tearDownModule(): + pass + + +class TestGetPredecessors(unittest.TestCase): + def test_0_predecessors(self): + parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[]" } + + self.assertEqual(predecessors(parset), []) + + def test_1_predecessor(self): + parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[L426528]" } + + self.assertEqual(predecessors(parset), [426528]) + + def test_2_predecessors(self): + parset = { PARSET_PREFIX + "Observation.Scheduler.predecessors": "[L426528,L1]" } + + self.assertEqual(sorted(predecessors(parset)), [1,426528]) + + +def parset_as_dict(filename): + parset = PyParameterSet(filename, False) + d = {} + for k in parset.keywords(): + d[k] = parset.getString(k) + + return d + + +class TestResourceIndicators(unittest.TestCase): + """ + The spec for the resource indicators is a draft at this point, + and the output is quite extensive (many parset keys), so + verification of the output is pending. + """ + + def test_preprocessing_pipeline(self): + parset = parset_as_dict("tRATaskSpecified.in_preprocessing") + r = resourceIndicatorsFromParset(parset) + + def test_correlator_observation(self): + parset = parset_as_dict("tRATaskSpecified.in_correlator") + r = resourceIndicatorsFromParset(parset) + + +class TestService(unittest.TestCase): + def setUp(self): + # Create a random bus + self.busname = "%s-%s" % (sys.argv[0], str(uuid.uuid4())[:8]) + self.bus = ToBus(self.busname, { "create": "always", "delete": "always", "node": { "type": "topic" } }) + self.bus.open() + self.addCleanup(self.bus.close) + + # Define the services we use + self.status_service = "%s/TaskStatus" % (self.busname,) + + # ================================ + # Setup mock parset service + # ================================ + + # Nr of parsets requested, to detect multiple requests for the same parset, or of superfluous parsets + self.requested_parsets = 0 + + def TaskSpecificationService( OtdbID ): + if OtdbID == 1: + predecessors = "[2,3]" + elif OtdbID == 2: + predecessors = "[3]" + elif OtdbID == 3: + predecessors = "[]" + else: + raise Exception("Invalid OtdbID: %s" % OtdbID) + + self.requested_parsets += 1 + + return { + "Version.number": "1", + PARSET_PREFIX + "Observation.ObsID": str(OtdbID), + PARSET_PREFIX + "Observation.Scheduler.predecessors": predecessors, + } + + parset_service = Service("TaskSpecification", TaskSpecificationService, busname=self.busname) + parset_service.start_listening() + self.addCleanup(parset_service.stop_listening) + + # ================================ + # Setup listener to catch result + # of our service + # ================================ + + listener = RATaskSpecifiedBusListener(busname=self.busname) + listener.start_listening() + self.addCleanup(listener.stop_listening) + + self.trigger = MethodTrigger(listener, "onTaskSpecified") + + def testNoPredecessors(self): + """ + Request the resources for a simulated obsid 3, with the following predecessor tree: + + 3 requires nothing + """ + with RATaskSpecified("OTDB.TaskSpecified", otdb_busname=self.busname, my_busname=self.busname) as jts: + # Send fake status update + with ToBus(self.status_service) as tb: + msg = EventMessage(content={ + "treeID": 3, + "state": "prescheduled", + "time_of_change": datetime.datetime(2016,1,1), + }) + tb.send(msg) + + # Wait for message to arrive + self.assertTrue(self.trigger.wait()) + + # Verify message + self.assertEqual(self.trigger.args[0], 3) + resourceIndicators = self.trigger.args[2] + self.assertNotIn("1", resourceIndicators) + self.assertNotIn("2", resourceIndicators) + self.assertIn("3", resourceIndicators); + + # Make sure we only requested one parset + self.assertEqual(self.requested_parsets, 1) + + def testPredecessors(self): + """ + Request the resources for a simulated obsid 1, with the following predecessor tree: + + 1 requires 2, 3 + 2 requires 3 + 3 requires nothing + """ + + with RATaskSpecified("OTDB.TaskSpecified", otdb_busname=self.busname, my_busname=self.busname) as jts: + # Send fake status update + with ToBus(self.status_service) as tb: + msg = EventMessage(content={ + "treeID": 1, + "state": "prescheduled", + "time_of_change": datetime.datetime(2016,1,1), + }) + tb.send(msg) + + # Wait for message to arrive + self.assertTrue(self.trigger.wait()) + + # Verify message + self.assertEqual(self.trigger.args[0], 1) + resourceIndicators = self.trigger.args[2] + self.assertIn("1", resourceIndicators); + self.assertIn("2", resourceIndicators); + self.assertIn("3", resourceIndicators); + + # Make sure we only requested exactly three parsets + self.assertEqual(self.requested_parsets, 3) + +def main(argv): + unittest.main() + +if __name__ == "__main__": + # run all tests + import sys + main(sys.argv[1:]) diff --git a/SAS/ResourceAssignment/Services/test/tRATaskSpecified.sh b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.sh new file mode 100755 index 0000000000000000000000000000000000000000..e8f7135ea210eefbd42b0ecd8adb19e532f027c8 --- /dev/null +++ b/SAS/ResourceAssignment/Services/test/tRATaskSpecified.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runctest.sh tRATaskSpecified diff --git a/SAS/ResourceAssignment/SystemStatusService/ssdbservice.ini b/SAS/ResourceAssignment/SystemStatusService/ssdbservice.ini index bb5d45837f46e0a77b01484160506f75ec44f805..26043a8ca85368a36056aa209adbece488d57ef5 100644 --- a/SAS/ResourceAssignment/SystemStatusService/ssdbservice.ini +++ b/SAS/ResourceAssignment/SystemStatusService/ssdbservice.ini @@ -1,5 +1,5 @@ [program:ssdbservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;ssdbservice' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec ssdbservice' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/SystemStatusService/test/test_datamonitorqueueservice_and_rpc.py b/SAS/ResourceAssignment/SystemStatusService/test/test_datamonitorqueueservice_and_rpc.py index 5adf460bdc0d06e017f7ac3b0f8a78fcb953dede..c2c81b0dd590a0686b737fa40caa35a58a34766f 100755 --- a/SAS/ResourceAssignment/SystemStatusService/test/test_datamonitorqueueservice_and_rpc.py +++ b/SAS/ResourceAssignment/SystemStatusService/test/test_datamonitorqueueservice_and_rpc.py @@ -91,7 +91,7 @@ try: # create and run the service with createService(busname=busname, servicename=servicename): # and run all tests - unittest.main(verbosity=2) + unittest.main() print "done testing" finally: diff --git a/SAS/Scheduler/src/Controller.cpp b/SAS/Scheduler/src/Controller.cpp index c93933922fd711175296f3c6b10eb8e0a12c511b..2342513510d8c2cece1b72ef98bf05334b534abf 100644 --- a/SAS/Scheduler/src/Controller.cpp +++ b/SAS/Scheduler/src/Controller.cpp @@ -3926,110 +3926,116 @@ void Controller::copyTask(unsigned int taskID) { // then gets the data product information and checks and sets the correct info for these dependencies in the task // These dependencies have to be checked and set before a task is set to SCHEDULED std::pair<unscheduled_reasons, QString> Controller::setInputFilesForPipeline(Task *pPipe) { - std::pair<unscheduled_reasons, QString> error; - std::vector<const Task *> predecessors; - const IDvector &predecessorIDs(pPipe->getPredecessors()); - if (!predecessorIDs.empty()) { - // collect all predecessor tasks in a vector for faster searching - const Task *predTask; - for (IDvector::const_iterator predit = predecessorIDs.begin(); predit != predecessorIDs.end(); ++ predit) { - predTask = data.getTask(predit->second, predit->first); - if (predTask) { - predecessors.push_back(predTask); - } - else { - error.first = PREDECESSOR_NOT_FOUND; - error.second = QString("The predecessor task ") + QString::number(predit->second) + " could not be found."; - return error; // one of the predecessors could not be found - } - } + std::pair<unscheduled_reasons, QString> error; + std::vector<const Task *> predecessors; + const IDvector &predecessorIDs(pPipe->getPredecessors()); + if (!predecessorIDs.empty()) { + // collect all predecessor tasks in a vector for faster searching + const Task *predTask; + for (IDvector::const_iterator predit = predecessorIDs.begin(); predit != predecessorIDs.end(); ++ predit) { + predTask = data.getTask(predit->second, predit->first); + if (predTask) { + predecessors.push_back(predTask); + } + else { + error.first = PREDECESSOR_NOT_FOUND; + error.second = QString("The predecessor task ") + QString::number(predit->second) + " could not be found."; + return error; // one of the predecessors could not be found + } + } - // task has predecessors, check all input data products identifications of this task and search the - // output data products of the predecessors for matching identifications - // if a match is found then copy the corresponding filenames and location arrays into the inputDataProduct of this task - // if no match then error condition data product not found in predecessor(s) tasks, task cannot be scheduled - std::map<dataProductTypes, TaskStorage::inputDataProduct> &inputDataProducts(pPipe->storage()->getInputDataProductsForChange()); - - int idxIt; - bool foundit; - std::map<dataProductTypes, TaskStorage::outputDataProduct>::const_iterator pit; - QString sapstr; - QStringList locations, filenames; - std::vector<bool> skipVec; - bool resetSkipVector, SyncSkipWithPredecessor; - - for (dataProductTypes dpType = _BEGIN_DATA_PRODUCTS_ENUM_; dpType < _END_DATA_PRODUCTS_ENUM_-1; dpType = dataProductTypes(dpType + 1)) { - if (pPipe->storage()->isInputDataProduktEnabled(dpType)) { // is this input data product type enabled? - TaskStorage::inputDataProduct &dp = inputDataProducts[dpType]; // also creates the record in the inputDataProducts map if it doesn't exist yet - resetSkipVector = (dp.skip.empty() /*|| (dp.skip.size() != (unsigned)dp.filenames.size())*/); // the skip vector should only be synchronized with the predecessor skip vector the first time (i.e. when it is not yet set) - storageVector storageVec; - - for (QStringList::const_iterator identit = dp.identifications.begin(); identit != dp.identifications.end(); ++identit) { - foundit = false; - for (std::vector<const Task *>::const_iterator predit = predecessors.begin(); predit != predecessors.end(); ++predit) { - if ((*predit)->hasStorage()) { - const TaskStorage *predStorage((*predit)->storage()); + // task has predecessors, check all input data products identifications of this task and search the + // output data products of the predecessors for matching identifications + // if a match is found then copy the corresponding filenames and location arrays into the inputDataProduct of this task + // if no match then error condition data product not found in predecessor(s) tasks, task cannot be scheduled + std::map<dataProductTypes, TaskStorage::inputDataProduct> &inputDataProducts(pPipe->storage()->getInputDataProductsForChange()); + + int idxIt; + bool foundit; + std::map<dataProductTypes, TaskStorage::outputDataProduct>::const_iterator pit; + QString sapstr; + QStringList locations, filenames; + std::vector<bool> skipVec; + bool resetSkipVector, SyncSkipWithPredecessor; + + for (dataProductTypes dpType = _BEGIN_DATA_PRODUCTS_ENUM_; dpType < _END_DATA_PRODUCTS_ENUM_-1; dpType = dataProductTypes(dpType + 1)) { + if (pPipe->storage()->isInputDataProduktEnabled(dpType)) { // is this input data product type enabled? + TaskStorage::inputDataProduct &dp = inputDataProducts[dpType]; // also creates the record in the inputDataProducts map if it doesn't exist yet + resetSkipVector = (dp.skip.empty() /*|| (dp.skip.size() != (unsigned)dp.filenames.size())*/); // the skip vector should only be synchronized with the predecessor skip vector the first time (i.e. when it is not yet set) + storageVector storageVec; + + for (QStringList::const_iterator identit = dp.identifications.begin(); identit != dp.identifications.end(); ++identit) { + foundit = false; + for (std::vector<const Task *>::const_iterator predit = predecessors.begin(); predit != predecessors.end(); ++predit) { + if ((*predit)->hasStorage()) { + const TaskStorage *predStorage((*predit)->storage()); const std::map<dataProductTypes, TaskStorage::outputDataProduct> &pred_output(predStorage->getOutputDataProducts()); - pit = pred_output.find(dpType); - if (pit != pred_output.end()) { - idxIt = pit->second.identifications.indexOf(*identit); - if (idxIt != -1) { // found? + pit = pred_output.find(dpType); + if (pit != pred_output.end()) { + idxIt = pit->second.identifications.indexOf(*identit); + if (idxIt != -1) { // found? storageVector predecessorStorageVec(predStorage->getStorageLocations(dpType)); - unsigned psz(predecessorStorageVec.size()); - if (psz != 0) { - // copy the filenames and locations pointed to by this identification to the input data product list of this task - if (pit->second.filenames.size() == pit->second.locations.size()) { + unsigned psz(predecessorStorageVec.size()); + if (psz != 0 || (*predit)->getOutputDataproductCluster() == "CEP4") { + // copy the filenames and locations pointed to by this identification to the input data product list of this task + if (pit->second.filenames.size() == pit->second.locations.size()) { if ((dpType == DP_CORRELATED_UV) || (dpType == DP_COHERENT_STOKES) || (dpType == DP_INCOHERENT_STOKES)) { // for these data product types copy only the files that have the corresponding SAP - const QString &identification(pit->second.identifications.at(idxIt)); - int i(identification.indexOf(".SAP")); - if (i != -1) { // does it contain a reference to a specific SAP? - sapstr = identification.mid(i+1, identification.indexOf('.',i+1) - i - 1); - SyncSkipWithPredecessor = (pit->second.skip.size() == (unsigned)pit->second.filenames.size()); - for (int i = 0; i < pit->second.filenames.size(); ++ i) { - const QString &filename(pit->second.filenames.at(i)); - if (filename.contains(sapstr)) { - filenames.push_back(filename); - locations.push_back(pit->second.locations.at(i)); - storageVec.push_back(predecessorStorageVec.at(i % psz)); - if (resetSkipVector) { - if (SyncSkipWithPredecessor) { - skipVec.push_back(pit->second.skip.at(i)); - } - else { - skipVec.push_back(false); - } - } - } - } - } - else { // no specific SAP specified in identification, just copy all files - filenames += pit->second.filenames; - locations += pit->second.locations; - storageVec.insert(storageVec.end(), predecessorStorageVec.begin(), predecessorStorageVec.end()); - if (resetSkipVector) { - if (pit->second.skip.size() == (unsigned)pit->second.filenames.size()) { - skipVec.insert(skipVec.end(), pit->second.skip.begin(), pit->second.skip.end()); - } - else { - skipVec.assign(filenames.size(), false); - } - } - } - } - else { // for all other data product types copy all files - filenames += pit->second.filenames; - locations += pit->second.locations; - storageVec.insert(storageVec.end(), predecessorStorageVec.begin(), predecessorStorageVec.end()); - if (resetSkipVector) { - if (pit->second.skip.size() == (unsigned)pit->second.filenames.size()) { - skipVec.insert(skipVec.end(), pit->second.skip.begin(), pit->second.skip.end()); - } - else { - skipVec.assign(filenames.size(), false); - } - } - } - // also set the input file size + const QString &identification(pit->second.identifications.at(idxIt)); + int i(identification.indexOf(".SAP")); + if (i != -1) { // does it contain a reference to a specific SAP? + sapstr = identification.mid(i+1, identification.indexOf('.',i+1) - i - 1); + SyncSkipWithPredecessor = (pit->second.skip.size() == (unsigned)pit->second.filenames.size()); + for (int i = 0; i < pit->second.filenames.size(); ++ i) { + const QString &filename(pit->second.filenames.at(i)); + if (filename.contains(sapstr)) { + filenames.push_back(filename); + locations.push_back(pit->second.locations.at(i)); + if ((*predit)->getOutputDataproductCluster() != "CEP4") { + storageVec.push_back(predecessorStorageVec.at(i % psz)); + } + if (resetSkipVector) { + if (SyncSkipWithPredecessor) { + skipVec.push_back(pit->second.skip.at(i)); + } + else { + skipVec.push_back(false); + } + } + } + } + } + else { // no specific SAP specified in identification, just copy all files + filenames += pit->second.filenames; + locations += pit->second.locations; + if ((*predit)->getOutputDataproductCluster() != "CEP4") { + storageVec.insert(storageVec.end(), predecessorStorageVec.begin(), predecessorStorageVec.end()); + } + if (resetSkipVector) { + if (pit->second.skip.size() == (unsigned)pit->second.filenames.size()) { + skipVec.insert(skipVec.end(), pit->second.skip.begin(), pit->second.skip.end()); + } + else { + skipVec.assign(filenames.size(), false); + } + } + } + } + else { // for all other data product types copy all files + filenames += pit->second.filenames; + locations += pit->second.locations; + if ((*predit)->getOutputDataproductCluster() != "CEP4") { + storageVec.insert(storageVec.end(), predecessorStorageVec.begin(), predecessorStorageVec.end()); + } + if (resetSkipVector) { + if (pit->second.skip.size() == (unsigned)pit->second.filenames.size()) { + skipVec.insert(skipVec.end(), pit->second.skip.begin(), pit->second.skip.end()); + } + else { + skipVec.assign(filenames.size(), false); + } + } + } + // also set the input file size const std::pair<double, unsigned> &outputSizes(predStorage->getOutputFileSizes(dpType)); pPipe->storage()->setInputFileSizes(dpType, std::pair<double, unsigned>(outputSizes.first, filenames.size())); @@ -4041,50 +4047,50 @@ std::pair<unscheduled_reasons, QString> Controller::setInputFilesForPipeline(Tas pPulsarPipe->setCoherentType(predObs->getRTCPsettings().coherentType); pPulsarPipe->setIncoherentType(predObs->getRTCPsettings().incoherentType); } - } - else { - std::cerr << "filenames and locations array of task: " << (*predit)->getID() << " for data product: " << DATA_PRODUCTS[dpType] << " are not equal in length!" << std::endl; - } - foundit = true; - break; // breaks out of predecessor search loop for the current identification - } - } - } + } + else { + std::cerr << "filenames and locations array of task: " << (*predit)->getID() << " for data product: " << DATA_PRODUCTS[dpType] << " are not equal in length!" << std::endl; + } + foundit = true; + break; // breaks out of predecessor search loop for the current identification + } + } + } } - } - if (!foundit) { - // one of the identifications was not found in any of the predecessor tasks - // this is an error, scheduling of this task cannot continue - error.first = INPUT_DATA_PRODUCT_NOT_FOUND; - error.second = QString("The task cannot be scheduled because the ") + DATA_PRODUCTS[dpType] + " input data product with identification:" + *identit + " could not be found in its predecessor tasks.\nAre all predecessor tasks correctly defined for this task?"; - return error; // identification not found - } - } - // set storage location IDs equal to the accumulation of the predecessor output storage vec's - pPipe->storage()->addInputStorageLocations(dpType, storageVec); - dp.filenames = filenames; - dp.locations = locations; - if (!resetSkipVector) { - if (dp.skip.size() != (unsigned)dp.filenames.size()) { - dp.skip.assign(dp.filenames.size(), false); - } - } - else { - dp.skip = skipVec; - } - filenames.clear(); - locations.clear(); - skipVec.clear(); - } - } - } - else { - error.first = INPUT_DATA_PRODUCT_NOT_FOUND; - error.second = QString("The task cannot be scheduled because it doesn't have a predecessor defined"); - return error; - } + } + if (!foundit) { + // one of the identifications was not found in any of the predecessor tasks + // this is an error, scheduling of this task cannot continue + error.first = INPUT_DATA_PRODUCT_NOT_FOUND; + error.second = QString("The task cannot be scheduled because the ") + DATA_PRODUCTS[dpType] + " input data product with identification:" + *identit + " could not be found in its predecessor tasks.\nAre all predecessor tasks correctly defined for this task?"; + return error; // identification not found + } + } + // set storage location IDs equal to the accumulation of the predecessor output storage vec's + pPipe->storage()->addInputStorageLocations(dpType, storageVec); + dp.filenames = filenames; + dp.locations = locations; + if (!resetSkipVector) { + if (dp.skip.size() != (unsigned)dp.filenames.size()) { + dp.skip.assign(dp.filenames.size(), false); + } + } + else { + dp.skip = skipVec; + } + filenames.clear(); + locations.clear(); + skipVec.clear(); + } + } + } + else { + error.first = INPUT_DATA_PRODUCT_NOT_FOUND; + error.second = QString("The task cannot be scheduled because it doesn't have a predecessor defined"); + return error; + } - return error; + return error; } bool Controller::doScheduleChecks(Task *pTask) { @@ -4538,6 +4544,9 @@ bool Controller::checkEarlyTasksStatus(void) { int treeID; for (std::vector<Task *>::const_iterator it = tasks.begin(); it != tasks.end(); ++it) { if ((*it)->getScheduledStart() <= now()) { + if (((*it)->getOutputDataproductCluster() == "CEP4") && (*it)->isPipeline()) { + continue; //Pipelines on CEP4: we don't care as SLURM sorts it out. + } treeID = (*it)->getSASTreeID(); if ((itsSASConnection->connect() == 0) && (treeID != 0)) { // only do the sas check for apparently too early tasks that are already in SAS , not for new tasks Task::task_status status(itsSASConnection->getTaskStatus(treeID)); diff --git a/SAS/Scheduler/src/SASConnection.cpp b/SAS/Scheduler/src/SASConnection.cpp index c33f4dd27294a16c114070092199953ebeedfdd6..66edc07fc541db8edb5932adbd0f5a806c52c7f9 100644 --- a/SAS/Scheduler/src/SASConnection.cpp +++ b/SAS/Scheduler/src/SASConnection.cpp @@ -2576,6 +2576,9 @@ bool SASConnection::saveStationSettings(int treeID, const StationTask &task, con bool SASConnection::saveInputStorageSettings(int treeID, const Task &task) { bool bResult(true); + if (task.getOutputDataproductCluster() == "CEP4") { //For CEP4 we're skipping this. /AR + return bResult; + } const TaskStorage *task_storage(task.storage()); if (task_storage) { const std::map<dataProductTypes, TaskStorage::inputDataProduct> &inputDataProducts(task_storage->getInputDataProducts()); diff --git a/SAS/Scheduler/src/blocksize.h b/SAS/Scheduler/src/blocksize.h index 359dcc8c97257f62c65411f388c39a1a76564c0c..31d60f5be845962af218f66f5c7c040bba9d5ca4 100644 --- a/SAS/Scheduler/src/blocksize.h +++ b/SAS/Scheduler/src/blocksize.h @@ -111,8 +111,8 @@ public: if (coherentStokesEnabled) { // Add required multiples for the CS Beamformer - // 16 * 64 (DelayAndBandPass.cu) - factor = lcm(factor, 16 * 64); + // 16 * 256 (DelayAndBandPass.cu) + factor = lcm(factor, 16 * 256); // 1024 is the maxNrThreadsPerBlock (CoherentStokesKernel.cc) // Note that coherenthannelsPerSubband is always a power of 2 < 1024 @@ -124,8 +124,8 @@ public: if (incoherentStokesEnabled) { // Add required multiples for the IS Beamformer - // 16 * 64 (DelayAndBandPass.cu) - factor = lcm(factor, 16 * 64); + // 16 * 256 (DelayAndBandPass.cu) + factor = lcm(factor, 16 * 256); // integration should fit (IncoherentStokes.cu) factor = lcm(factor, incoherentTimeIntegrationFactor * incoherentChannelsPerSubband); diff --git a/SAS/XML_generator/src/xmlgen.py b/SAS/XML_generator/src/xmlgen.py index e5aba9e56830d4d297b2d8e2b8e4bb5cc4fa7173..43d2f30b398a16f5adfc55041bf6b0e2ae4f938e 100755 --- a/SAS/XML_generator/src/xmlgen.py +++ b/SAS/XML_generator/src/xmlgen.py @@ -29,7 +29,7 @@ # URL : $URL: https://svn.astron.nl/ROD/trunk/LOFAR_Scheduler/DataHandler.cpp $ -VERSION = "2.16.6" +VERSION = "2.17.0" import sys, getopt, time from xml.sax.saxutils import escape as XMLescape diff --git a/SubSystems/CMakeLists.txt b/SubSystems/CMakeLists.txt index e3ed89ab0dee1d1ea9c8d898cd17ed04f604a44b..b654ea67287f6179bbaccd8d1e9008090102ee9a 100644 --- a/SubSystems/CMakeLists.txt +++ b/SubSystems/CMakeLists.txt @@ -13,3 +13,5 @@ lofar_add_package(SAS_Tools) lofar_add_package(PVSS_DB) lofar_add_package(LAPS_CEP) lofar_add_package(RAServices) +lofar_add_package(DataManagement) +lofar_add_package(Dragnet) diff --git a/SubSystems/DataManagement/CMakeLists.txt b/SubSystems/DataManagement/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..99e70b6f669fe3a4436c62d73c28810a4d38b5a0 --- /dev/null +++ b/SubSystems/DataManagement/CMakeLists.txt @@ -0,0 +1,10 @@ +# $Id: CMakeLists.txt 20934 2012-05-15 09:26:48Z schoenmakers $ + +lofar_package(DataManagement + DEPENDS CleanupService + StorageQueryService) + +# supervisord config files +install(FILES + DataManagement.ini + DESTINATION etc/supervisord.d) diff --git a/SubSystems/DataManagement/DataManagement.ini b/SubSystems/DataManagement/DataManagement.ini new file mode 100644 index 0000000000000000000000000000000000000000..9de2323567f39ca7c320fb0c92089921ade905ca --- /dev/null +++ b/SubSystems/DataManagement/DataManagement.ini @@ -0,0 +1,3 @@ +[group:DataManagementServices] +programs=cleanupservice,storagequeryservice +priority=200 diff --git a/SubSystems/Dragnet/CMakeLists.txt b/SubSystems/Dragnet/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f70557fc764036bc47507881f7db6ab9b7c7e89b --- /dev/null +++ b/SubSystems/Dragnet/CMakeLists.txt @@ -0,0 +1,5 @@ +# $Id$ + +# Dragnet: LOFAR software install for the LOFAR DRAGNET cluster +lofar_package(Dragnet DEPENDS Online_Cobalt Offline ObservationStartListener) + diff --git a/SubSystems/Dragnet/scripts/LOFAR-Dragnet-activate.sh b/SubSystems/Dragnet/scripts/LOFAR-Dragnet-activate.sh new file mode 100755 index 0000000000000000000000000000000000000000..50e391b4becde2898e77b63516e57a730abc9a42 --- /dev/null +++ b/SubSystems/Dragnet/scripts/LOFAR-Dragnet-activate.sh @@ -0,0 +1,35 @@ +#!/bin/bash -ex +# LOFAR-Dragnet-activate.sh +# +# LOFAR DRAGNET software activation. Repoint 'current' symlink to already deployed LOFAR release. +# +# Jenkins shell command: +# svn export https://svn.astron.nl/LOFAR/trunk/SubSystems/Dragnet/scripts/LOFAR-Dragnet-activate.sh && \ +# ./LOFAR-Dragnet-activate.sh $JENKINS_RELEASE && rm LOFAR-Dragnet-activate.sh +# +# $Id$ + +# config: version, paths, hostnames +lofar_version=2.17.5 # "x.y.z" (release tag version) +lofar_release=LOFAR-Release-$(echo $lofar_version | tr . _) # svn tag name example: LOFAR-Release-2_17_5 +lofar_versions_root=/opt/lofar_versions +prefix=$lofar_versions_root/$lofar_release +nodelist="dragnet dragproc $(seq -s ' ' -f drg%02g 1 23)" + +# repoint 'current' symlink to release version atomically (using mv -T) +declare -a status_arr3 +declare arr3_i=0 +for host in $nodelist; do + # Escape double quotes below the following line! And use \ at newline and don't use '#' comments across ssh as lofarbuild uses tcsh... + ssh -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no $host " + hostname && \ + cd $lofar_versions_root && \ + ln -sfT $lofar_release current_tmp && mv -fT current_tmp current && \ + sync + " >&2 & + status_arr3[$arr3_i]=$! + ((arr3_i++)) || true +done +for ((i = 0; i < $arr3_i; i++)); do + wait ${status_arr3[$i]} +done diff --git a/SubSystems/Dragnet/scripts/LOFAR-Dragnet-deploy.sh b/SubSystems/Dragnet/scripts/LOFAR-Dragnet-deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..5231e35f0da9b5595f3cd2664aee282212e9d623 --- /dev/null +++ b/SubSystems/Dragnet/scripts/LOFAR-Dragnet-deploy.sh @@ -0,0 +1,88 @@ +#!/bin/bash -ex +# LOFAR-Dragnet-deploy.sh +# +# LOFAR software build and deploy for the LOFAR DRAGNET cluster. +# Does not activate deployed software (i.e. does not repoint 'current' symlink). See LOFAR-Dragnet-activate.sh +# +# Jenkins shell command: +# svn export https://svn.astron.nl/LOFAR/trunk/SubSystems/Dragnet/scripts/LOFAR-Dragnet-deploy.sh && \ +# ./LOFAR-Dragnet-deploy.sh $JENKINS_RELEASE && rm LOFAR-Dragnet-deploy.sh +# +# $Id$ + +# unload all loaded env modules to avoid accidentally depending on pkgs in PATH, LD_LIBRARY_PATH, ... +module purge || true + +# config: version, paths, hostnames +lofar_version=2.17.5 # "x.y.z" (release tag version) +lofar_release=LOFAR-Release-$(echo $lofar_version | tr . _) # svn tag name example: LOFAR-Release-2_17_5 +lofar_versions_root=/opt/lofar_versions +prefix=$lofar_versions_root/$lofar_release +nodelist="dragnet dragproc $(seq -s ' ' -f drg%02g 1 23)" +tmpdir=`mktemp -d 2>/dev/null || mktemp -d -t tempdir` # GNU/Linux and Mac OS X compat mktemp usage +buildtype=gnu_optarch # optarch enables -O3 -march=native + +# check out release. +cd "$tmpdir" +svn checkout https://svn.astron.nl/LOFAR/tags/$lofar_release LOFAR > /dev/null + +# build, install into DESTDIR, and create deploy archive +mkdir -p $buildtype && cd $_ +cmake -DBUILD_PACKAGES=Dragnet -DCMAKE_INSTALL_PREFIX=$prefix ../LOFAR +make -j 8 install DESTDIR="$tmpdir"/destdir +archive=$lofar_release-dragnet.tgz +tar zcf $archive destdir/* # whole $prefix path ends up in archive entries + +# create environment module file +envmodfilename=$lofar_version +echo '#%Module 1.0' >> $envmodfilename +echo 'module-whatis "Adds the ASTRON LOFAR tree (NDPPP, BBS, awimager, pybdsm, ...) release '$lofar_version' to your environment (do not mix with sourcing lofarinit.sh)"' >> $envmodfilename +echo 'conflict lofar' >> $envmodfilename +echo 'prepend-path PATH '$prefix'/bin:'$prefix'/sbin' >> $envmodfilename +echo 'prepend-path LD_LIBRARY_PATH '$prefix'/lib' >> $envmodfilename +echo 'prepend-path PYTHONPATH '$prefix'/lib64/python2.7/site-packages' >> $envmodfilename +echo 'setenv LOFARROOT '$prefix >> $envmodfilename +echo '#setenv LOFARDATAROOT /opt/lofar/data' >> $envmodfilename + +# create archive and copy across cluster +declare -a status_arr1 +declare arr1_i=0 +for host in $nodelist; do + scp -p -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no $archive $host:$lofar_versions_root/ & + status_arr1[$arr1_i]=$! + ((arr1_i++)) || true +done +for ((i = 0; i < $arr1_i; i++)); do + wait ${status_arr1[$i]} +done + +# Unpack and set up across cluster (requires $prefix and /etc/modulefiles/lofar/ to be writable). +# The archived files all have full pathname, so unpack from root dir; -m: don't warn on timestamping /opt +# Need to replace created var/ subdirs by symlink to common var/ dir. +# The sudo setcap cmds reqs a sudoers.d/ file in place to allow lofarbuild to do this without auth. +declare -a status_arr2 +declare arr2_i=0 +for host in $nodelist; do + # Escape double quotes below the following line! And use \ at newline and don't use '#' comments across ssh as lofarbuild uses tcsh... + ssh -t -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no $host " + hostname && \ + cd $lofar_versions_root && \ + mv $envmodfilename /etc/modulefiles/lofar/ && \ + rm -rf $lofar_release && \ + cd / && tar zxfm $lofar_versions_root/$archive && \ + rm -rf $prefix/var && \ + ln -sfT /home/lofarsys/lofar/var $prefix/var && \ + sudo -n /sbin/setcap cap_net_raw,cap_sys_nice,cap_ipc_lock=ep $prefix/bin/rtcp && \ + sudo -n /sbin/setcap cap_net_raw,cap_sys_nice,cap_ipc_lock=ep $prefix/bin/outputProc && \ + sudo -n /sbin/setcap cap_net_raw,cap_sys_nice,cap_ipc_lock=ep $prefix/bin/TBB_Writer && \ + sync + " >&2 & + status_arr2[$arr2_i]=$! + ((arr2_i++)) || true +done +for ((i = 0; i < $arr2_i; i++)); do + wait ${status_arr2[$i]} +done + +cd +rm -rf "$tmpdir" diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/CheckIERS.cc b/SubSystems/Dragnet/scripts/casacore-measures-tables/CheckIERS.cc new file mode 100644 index 0000000000000000000000000000000000000000..6e5879794cc4cdf1c710b03b3cf7eaa891d0bdd7 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/CheckIERS.cc @@ -0,0 +1,89 @@ +// CheckIERS program to find age of IERS tables. +// +// $Id$ +// +// Program CheckIERS checks the last entry in the IERSpredict table and +// compares this with the current day (all in MJD). +// The predict table should have entries for the coming period. If it only has +// entries in the past, the table is outdated. +// +// Usage: CheckIERS dir=<root dir of IERS tables> +// +// Options: all: age of last entry in all IERS tables +// verbose: Show readable text, instead of just numbers +// +// The root directory is default set to /localhome/IERS; the +// default location for the IERS tables is in directory 'geodetic' under +// the IERS root directory. +// +// If only the age of the table must be extracted, parse the output of this +// program like this: +// +// > CheckIERS | tail -1 | awk '{print $6}' +// +// Author: A.P. Schoenmakers +// + +#include <iostream> +#include <casacore/casa/Exceptions.h> +#include <casacore/casa/Inputs/Input.h> +#include <casacore/tables/Tables.h> +#include <casacore/casa/OS/Time.h> + +using namespace casa; + +int main(int argc, char *argv[]) { + try + { + Input inputs; + inputs.version(""); + inputs.create("dir","/localhome/IERS","root dir of IERS (default: /localhome/IERS)","String"); + inputs.create("table","IERSpredict","IERS table to check","String"); + inputs.create("verbose","False","Verbose output","Bool","verbose"); + inputs.create("all","False","Use all IERS tables","Bool","all"); + inputs.readArguments(argc, argv); + + String dir = inputs.getString("dir"); + String table = inputs.getString("table"); + Bool verbose = inputs.getBool("verbose"); + Bool all_tables = inputs.getBool("all"); + std::vector<String> tables; + if (all_tables == True) { + tables.push_back("IERSeop2000"); + tables.push_back("IERSeop97"); + tables.push_back("IERSpredict2000"); + tables.push_back("IERSpredict"); + } else { + tables.push_back(table); + } + + for (std::vector<String>::iterator it = tables.begin() ; it != tables.end(); ++it) { + String fullname = dir + "/geodetic/" + *it; + + if (verbose) cout << "Checking table " << fullname << endl; + Table ierstable(fullname); + + uint lastLine = ierstable.nrow(); + //cout << "Number of lines: " << lastLine << endl; + + ROTableColumn MJDcol(ierstable,"MJD"); + + int eopMJD = (int)MJDcol.asdouble(lastLine-1); + Time curMJDtime; + int curMJD = (int)curMJDtime.modifiedJulianDay(); + if (verbose) { + cout << "Current MJD is : " << curMJD << endl; + cout << "Last MJD in " << *it << " : " << eopMJD << endl; + cout << "Nr of days remaining: " << eopMJD - curMJD << endl; + } else { + cout << eopMJD - curMJD << endl; + } + } + } + catch(AipsError& err) + { + cerr << "Aips++ error detected: " << err.getMesg() << endl; + return 1; + }; + +} diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/Makefile b/SubSystems/Dragnet/scripts/casacore-measures-tables/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..81eb7e41ca98e8af3ca61b993cb439c7e14f22e9 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/Makefile @@ -0,0 +1,36 @@ +# Stupid Makefile for program CheckIERS +# +# Valid for kis001 +# +# $Id$ +# +# Uses weekly casacore. +# +CFLAGS = -g -Wall +CC = g++ +AIPSFLAGS = -DAIPS -DAIPS_LINUX -DAIPS_LITTLE_ENDIAN -DAIPS_AUTO_STL -DAIPS_NO_TEMPLATE_SRC +#AIPSLIBD = -L/data/casacore/weekly/casacore/lib +AIPSLIBD = -L/opt/casacore/lib -Wl,-rpath=/opt/casacore/lib +AIPSLIBS = -lcasa_measures -lcasa_tables -lcasa_casa +#AIPSINCD = -I/data/casacore/weekly/casacore/include/casacore +AIPSINCD = -I/opt/casacore/include + +INCLUDES = -I. $(AIPSINCD) +LIBS = $(AIPSLIBD) $(AIPSLIBS) -ldl +FLAGS = $(AIPSFLAGS) +OBJS = CheckIERS.o +SRCS = CheckIERS.cc + +all: CheckIERS + +CheckIERS: $(OBJS) ${HDRS} + ${CC} ${CFLAGS} ${FLAGS} ${INCLUDES} -o $@ $(OBJS) ${LIBS} + +.cc.o: + ${CC} ${CFLAGS} ${FLAGS} ${INCLUDES} -c $< + +depend: + makedepend ${SRCS} + +clean: + rm *.o diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/apply_casacore_measures_data.sh b/SubSystems/Dragnet/scripts/casacore-measures-tables/apply_casacore_measures_data.sh new file mode 100755 index 0000000000000000000000000000000000000000..44892177d58105121e960f8656ed2b041c181ce6 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/apply_casacore_measures_data.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# apply_casacore_measures_tables.sh +# +# Install downloaded casacore measures tables atomically and verify it is in use. +# +# $Id$ + +DIR="${BASH_SOURCE%/*}" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/casacore_measures_common.sh" + + +module add casacore # add casacore bin dir to PATH to run the findmeastable program + +update_id=$(get_latest) + +# If not already in use, switch current symlink to the latest $dir_prefix/ *atomically* with an mv. Avoids race with a reader. +if [ "`readlink current`" != "$update_id" ]; then + log INFO "Applying $update_id" + ln -s "$update_id" "$working_dir/current_${update_id}" && mv -Tf "$working_dir/current_${update_id}" "$working_dir/current" +else + log INFO "No new table to apply." +fi + +# Check if casacore uses the just set up tables by extracting the path (token(s) 6,...) from findmeastable. +# If ok, findmeastable prints: "Measures table Observatories found as /home/jenkins/root/share/casacore/data/geodetic/Observatories" +if ! findmeastable > /dev/null; then exit 1; fi +used_dir=`findmeastable | cut -d' ' -f 6-` +if [ $? -ne 0 ]; then exit 1; fi +used_path=`readlink -f "$used_dir/../../.."` +if [ $? -ne 0 ]; then exit 1; fi +update_id_path="$working_dir/$update_id" +if [ "$update_id_path" != "$used_path" ]; then + # potential improvement: revert if applied (and if it used to work) (e.g. empty $update_id/) + fatal "It appears that the most recently set up measures tables are not in use. Most recent on the system is: '$update_id'." +fi + +remove_old + +log INFO "Done. The most recently retrieved measures data is (now) in use." diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/casacore_measures_common.sh b/SubSystems/Dragnet/scripts/casacore-measures-tables/casacore_measures_common.sh new file mode 100755 index 0000000000000000000000000000000000000000..85f70072cac2fd7c317d11a4c1a6b75a011cf9a4 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/casacore_measures_common.sh @@ -0,0 +1,55 @@ +# casacore_measures_common.sh +# Source this file, don't execute it! +# +# $Id$ + +working_dir=/opt/IERS +dir_prefix=IERS- +measures_ftp_path=ftp://ftp.astron.nl/outgoing/Measures +measures_data_filename=WSRT_Measures.ztar +#measures_md5sum_filename=$measures_data_filename.md5sum +hostname=$(hostname) + +# Example log() usage: log INFO "foo bar" +# writes to stderr something like: node42 2015-10-16 16:00:46,186 INFO - foo bar +log() { + loglevel=$1 # one of: DEBUG INFO WARN ERROR FATAL + message=$2 + ts=`date --utc '+%F %T,%3N'` # e.g. 2015-10-16 16:00:46,186 + echo "$hostname $ts $loglevel - $message" >&2 +} + +fatal() { + log FATAL "$1" + exit 1 +} + +# echo the directory component name of the most recent measures table. +# 'Most recent' is from name, not from last modified date! +get_latest() { + if ! cd "$working_dir"; then fatal "Failed to cd to $working_dir"; fi + local update_id + update_id=`ls -d "$dir_prefix"* 2> /dev/null | tail -n 1` + if [ -z "$update_id" ]; then + # fatal since this function is to be called after an update has been retrieved and + # unpacked (but not yet activated via the symlink). + log FATAL "No existing casacore measures install recognized."; + fi + cd "$OLDPWD" # 'cd -' echos the path altering func output :| + echo "$update_id" +} + +# Remove earlier downloaded entries beyond the 4 latest. ('ls' also sorts.) +remove_old() { + if ! cd "$working_dir"; then fatal "Failed to cd to $working_dir"; fi + old_update_ids=`ls -d "$dir_prefix"* 2> /dev/null | head -n -4` + if [ ! -z "$old_update_ids" ]; then + rm -rf $old_update_ids + if [ $? -ne 0 ]; then + log WARN "Failed to remove old measure tables dir(s)." # not fatal + else + log INFO "Removed old measure tables dir(s): $old_update_ids" # var contains newlines... + fi + fi + cd "$OLDPWD" # 'cd -' echos the path altering func output :| +} diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/cron-update-IERS-DRAGNET.sh b/SubSystems/Dragnet/scripts/casacore-measures-tables/cron-update-IERS-DRAGNET.sh new file mode 100755 index 0000000000000000000000000000000000000000..1499e22e2abf2d4a6eeb54e3fb50b21ee9112750 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/cron-update-IERS-DRAGNET.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# cron-update-IERS-DRAGNET.sh +# +# Updates casacore measures tables on the DRAGNET cluster under /opt/IERS/ +# To be started once per week (or so) via cron as 'lofarsys:dragnet' on any single node. +# lofarsys' crontab (every Mon, 04:00 AM): +# 0 4 * * 1 /opt/IERS/cron-update-IERS-DRAGNET.sh 2> /home/lofarsys/lofar/var/log/IERS/cron-update-IERS-DRAGNET.log +# +# Caveats: If any cluster node is unreachable, the update is not applied. +# This is intentional, but when at least one node is down often, this is unworkable. +# The solution is *not* a partial install, but to create a proper software package +# that every node pulls on a fixed time in the week and upon boot before starting slurmd. +# +# $Id$ + +DIR="${BASH_SOURCE%/*}" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/casacore_measures_common.sh" + + +nodelist="dragnet dragproc $(seq -s ' ' -f drg%02g 1 23)" +log INFO "Started. Node list: $nodelist" + +log INFO "Retrieving fresh copy of casacore measures archive file" +"$working_dir/get_casacore_measures_data.sh" || exit 1 + +latest=$(get_latest) +path="$working_dir/$latest/data/$measures_data_filename" +log INFO "Transferring archive '$path' across the cluster" +declare -a status_arr1 +declare arr1_i=0 +for host in $nodelist; do + scp -p -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no "$path" "$host:$working_dir" & + status_arr1[$arr1_i]=$! + ((arr1_i++)) || true +done +for ((i = 0; i < $arr1_i; i++)); do + log DEBUG "Waiting for pid ${status_arr1[$i]}" + wait ${status_arr1[$i]} || exit 1 +done + +log INFO "Putting archive content in place on other nodes" +declare -a status_arr2 +declare arr2_i=0 +for host in $nodelist; do + # Escape double quotes below the following line! + ssh -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no $host " + hostname && + cd /opt/IERS && + mkdir -p \"$latest/data\" && + mv \"$measures_data_filename\" \"$latest/data\" && + cd \"$latest/data\" && + tar zxf \"$measures_data_filename\" && + cd ../.. && + chgrp -R dragnet \"$latest\" + " >&2 & + status_arr2[$arr2_i]=$! + ((arr2_i++)) || true +done +for ((i = 0; i < $arr2_i; i++)); do + log DEBUG "Waiting for pid ${status_arr2[$i]}" + wait ${status_arr2[$i]} || exit 1 +done + +log INFO "Applying update across the cluster" +declare -a status_arr3 +declare arr3_i=0 +for host in $nodelist; do + # Escape double quotes below the following line! + ssh -q -o BatchMode=yes -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no $host " + hostname && + \"$working_dir/apply_casacore_measures_data.sh\" + " >&2 & + status_arr3[$arr3_i]=$! + ((arr3_i++)) || true +done +for ((i = 0; i < $arr3_i; i++)); do + log DEBUG "Waiting for pid ${status_arr3[$i]}" + wait ${status_arr3[$i]} || exit 1 +done + +log INFO "Done" diff --git a/SubSystems/Dragnet/scripts/casacore-measures-tables/get_casacore_measures_data.sh b/SubSystems/Dragnet/scripts/casacore-measures-tables/get_casacore_measures_data.sh new file mode 100755 index 0000000000000000000000000000000000000000..c53b32665834b4fed3bbca00ddfb50296a4c5244 --- /dev/null +++ b/SubSystems/Dragnet/scripts/casacore-measures-tables/get_casacore_measures_data.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# get_casacore_measures_tables.sh +# +# Retrieve new casacore measures tables under $working_dir and extract. Written for jenkins@fs5 (DAS-4). +# If it works out, remove very old download dirs starting with $dir_prefix. +# +# $Id$ + +DIR="${BASH_SOURCE%/*}" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/casacore_measures_common.sh" + + +# Get the data (~12 MB) from the server. This may take up to 10s of seconds for a slow FTP server. +# By default, when wget downloads a file, the timestamp is set to match the timestamp from the remote file. +download() +{ + wget -N --tries=4 \ + $measures_ftp_path/$measures_data_filename #\ + #$measures_ftp_path/$measures_md5sum_filename +} + +# Verify that md5 hash is equal to hash in $measures_md5sum_filename (no longer used, was used for measures tables from CSIRO) +# No need to compare the filename. (If from CSIRO, note that the .md5sum contains a CSIRO path.) +check_md5() +{ + local md5sum=`cut -f 1 -d ' ' $measures_md5sum_filename` + if [ $? -ne 0 ]; then return 1; fi + local data_md5=`md5sum $measures_data_filename | cut -f 1 -d ' '` + if [ -z "$data_md5" ]; then return 1; fi + + if [ "$md5sum" != "$data_md5" ]; then + log ERROR "Computed and downloaded MD5 sums do not match." + return 1 + fi + + return 0 +} + +# Check that the measures tables under tmp_$update_id/data are up-to- date. +check_uptodate() +{ + # Only check last int, but set options all and verbose as example cmd to manually run on error. + check_out=$("$working_dir"/CheckIERS dir="$update_id/data" all=true verbose=true) || return 1 + nrdays=$(echo "$check_out" | tail -1 | awk '{print $5}') || return 1 + [ $nrdays -ge 7 ] # IERS prediction for at least 7 more days? +} + + +update_id=$dir_prefix`date --utc +%FT%T.%N` # e.g. IERS-2015-09-26T01:58:30.098006623 +if [ $? -ne 0 ]; then exit 1; fi + +# Use a tmp_ name until written and checked, then move into place. +if ! cd "$working_dir" || ! mkdir -p "tmp_$update_id/data" || ! cd "tmp_$update_id/data"; then + fatal "Failed to create and cd to tmp_$update_id/data" +fi +if ! download; then # || ! check_md5; then + rm -f $measures_data_filename + log ERROR "Download failed. Retrying once." + sleep 2 + if ! download; then # || ! check_md5; then + rm -f $measures_data_filename #$measures_md5sum_filename + cd ../.. && rm -rf "tmp_$update_id" + fatal "Download failed again." + exit 1 + fi +fi + +if ! tar zxf $measures_data_filename; then + cd ../.. && rm -rf "tmp_$update_id" + exit 1 +fi + +cd ../.. || exit 1 # back to $working_dir + +# Make it available to the apply script to install/move it at an opportune moment. +if ! mv "tmp_$update_id" "$update_id"; then + # On error, leave the tmp_ dir in place for manual inspection. + fatal "Failed to prepare measures tables update 'tmp_$update_id'. Manual intervention required." +fi + +if ! check_uptodate; then fatal "Verification of downloaded measures tables failed."; fi + +log INFO "Done. Measures tables update '$update_id' ready to be applied." diff --git a/SubSystems/Offline/CMakeLists.txt b/SubSystems/Offline/CMakeLists.txt index f11418570ff5891292fe2deb87bfe63fa7004afe..1866393aea46e78662c29469bcadacb5ffe58f96 100644 --- a/SubSystems/Offline/CMakeLists.txt +++ b/SubSystems/Offline/CMakeLists.txt @@ -1,4 +1,4 @@ # $Id$ -lofar_package(Offline DEPENDS CEP MSLofar StaticMetaData MessageBus) +lofar_package(Offline DEPENDS CEP MSLofar StaticMetaData MessageBus OTDB_Services) diff --git a/SubSystems/Online_Cobalt/install/lofarsys/crontab b/SubSystems/Online_Cobalt/install/lofarsys/crontab new file mode 100644 index 0000000000000000000000000000000000000000..6bfa90d6e7043152f7e43ccfe00f5d41f25c8b48 --- /dev/null +++ b/SubSystems/Online_Cobalt/install/lofarsys/crontab @@ -0,0 +1,3 @@ +# m h dom mon dow command +* * * * * source /opt/lofar/lofarinit.sh && ping_intl.sh >> $HOME/ping_intl.`hostname`.log 2>&1 + diff --git a/SubSystems/Online_Cobalt/install/postinstall_lofarsys.sh b/SubSystems/Online_Cobalt/install/postinstall_lofarsys.sh index c91db6aeb7218f98467d5d8ed134390207c783ee..21f9e857155fdcb23f62fee6148410cfaddd5681 100755 --- a/SubSystems/Online_Cobalt/install/postinstall_lofarsys.sh +++ b/SubSystems/Online_Cobalt/install/postinstall_lofarsys.sh @@ -31,3 +31,9 @@ ssh localhost true # ******************************************** echo "Configuring /opt/lofar/var..." mkdir -p ~lofarsys/lofar/var/{log,run} + +# ******************************************** +# Schedule periodic tasks +# ******************************************** +echo "Configuring crontab..." +crontab lofarsys/crontab diff --git a/SubSystems/Online_Cobalt/validation/cep4/docker/9311-cgroupdriver.test b/SubSystems/Online_Cobalt/validation/cep4/docker/9311-cgroupdriver.test new file mode 100755 index 0000000000000000000000000000000000000000..bc87ad3e5fa236613684869db767c85fc3356509 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/docker/9311-cgroupdriver.test @@ -0,0 +1,11 @@ +#/bin/bash -e + +# Test for cgroupdriver=cgroupfs workaround for Docker 1.9.1 + +DOCKER_VERSION=`docker --version | awk '{ print $3; }'` + +[ "$DOCKER_VERSION" == "1.9.1," ] || exit 0 + +# Docker startup line must contain "--exec-opt=native.cgroupdriver=cgroupfs" +fgrep "ExecStart" /usr/lib/systemd/system/docker.service | fgrep -- "--exec-opt=native.cgroupdriver=cgroupfs" || exit 1 + diff --git a/SubSystems/Online_Cobalt/validation/cep4/docker/datalocation.test b/SubSystems/Online_Cobalt/validation/cep4/docker/datalocation.test new file mode 100755 index 0000000000000000000000000000000000000000..0d4666d30e4dfd34b34afaa07f379420dcbfd09d --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/docker/datalocation.test @@ -0,0 +1,9 @@ +#/bin/bash -e + +# Test for location of Docker's data: head nodes need to use /localdata/docker + +[[ `hostname` =~ "head" ]] || exit 0 + +# Docker startup line must contain "--graph=/localdata/docker" +fgrep "ExecStart" /usr/lib/systemd/system/docker.service | fgrep -- "--graph=/localdata/docker" || exit 1 + diff --git a/SubSystems/Online_Cobalt/validation/cep4/lofarsys/docker.root.test b/SubSystems/Online_Cobalt/validation/cep4/lofarsys/docker.root.test new file mode 100755 index 0000000000000000000000000000000000000000..6830d60c8cc9c712d446e10e2b7003f39625bfec --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/lofarsys/docker.root.test @@ -0,0 +1,3 @@ +#!/bin/bash + +sudo -u lofarsys docker run --rm -it hello-world || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/lofarsys/ssh_localhost.root.test b/SubSystems/Online_Cobalt/validation/cep4/lofarsys/ssh_localhost.root.test new file mode 100755 index 0000000000000000000000000000000000000000..1f58c732d0cf127e9a79cc053236db1a8dc6bbfb --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/lofarsys/ssh_localhost.root.test @@ -0,0 +1,3 @@ +#!/bin/bash + +sudo -u lofarsys ssh localhost /bin/true || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/network/9228-ipoib.test b/SubSystems/Online_Cobalt/validation/cep4/network/9228-ipoib.test new file mode 100755 index 0000000000000000000000000000000000000000..27d5b1c0b1a7f89f2c3c0c33169d95e4a81801ff --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/network/9228-ipoib.test @@ -0,0 +1,15 @@ +#!/bin/bash + +for IFACE in ib0 +do + echo Testing interface $IFACE... + + # Interface should exist + ip link show $IFACE || exit 1 + + # Interface should be up + ip link show $IFACE | grep -q "state UP" || exit 1 + + # Connected mode should be set + [ "`cat /sys/class/net/$IFACE/mode`" == "connected" ] || exit 1 +done diff --git a/SubSystems/Online_Cobalt/validation/cep4/os/9226-utc.test b/SubSystems/Online_Cobalt/validation/cep4/os/9226-utc.test new file mode 100755 index 0000000000000000000000000000000000000000..ebbfe9d0761ebdc38fa6f98c56f575b41af8c425 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/os/9226-utc.test @@ -0,0 +1,3 @@ +#!/bin/bash + +[ "`date +%Z`" == "UTC" ] || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/os/9376-hostname.test b/SubSystems/Online_Cobalt/validation/cep4/os/9376-hostname.test new file mode 100755 index 0000000000000000000000000000000000000000..e57b2d94c956c157e845f699ce7ac3cee6ccaa46 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/os/9376-hostname.test @@ -0,0 +1,14 @@ +#!/bin/bash + +FQDN=`hostname --fqdn` + +# FQDN hostname must give full-length name +case $FQDN in + *.cep4.control.lofar) + exit 0 + ;; + *) + exit 1 + ;; +esac + diff --git a/SubSystems/Online_Cobalt/validation/cep4/os/lustre-mounted.test b/SubSystems/Online_Cobalt/validation/cep4/os/lustre-mounted.test new file mode 100755 index 0000000000000000000000000000000000000000..03ccef9e2a6f921679cc2cb073536a0da12c5687 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/os/lustre-mounted.test @@ -0,0 +1,3 @@ +#!/bin/bash + +mount -t lustre | fgrep -q "/data" || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/os/services.test b/SubSystems/Online_Cobalt/validation/cep4/os/services.test new file mode 100644 index 0000000000000000000000000000000000000000..f9760a5e6b812f311710f65dc82269397cbcb98d --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/os/services.test @@ -0,0 +1,4 @@ +#!/bin/bash + +# No services should have failed, except for network.service +systemctl | fgrep -v "network.service" | grep failed diff --git a/SubSystems/Online_Cobalt/validation/cep4/os/users.test b/SubSystems/Online_Cobalt/validation/cep4/os/users.test new file mode 100755 index 0000000000000000000000000000000000000000..8935075222ca2f538b7783817eff83769c626c9f --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/os/users.test @@ -0,0 +1,11 @@ +#!/bin/bash + +# Restrict the users allowed on the CPU nodes +[[ `hostname` =~ "cpu" ]] || exit 0 + +# lofarsys should exist +id lofarsys || exit 1 + +# mol should not exist +id mol && exit 1 + diff --git a/SubSystems/Online_Cobalt/validation/cep4/packages/iperf.test b/SubSystems/Online_Cobalt/validation/cep4/packages/iperf.test new file mode 100644 index 0000000000000000000000000000000000000000..7885c278818fd01fc03cbf91d12e30b358b4051c --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/packages/iperf.test @@ -0,0 +1,2 @@ +#!/bin/bash +yum -C list installed iperf || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/packages/jenkins-reqs.test b/SubSystems/Online_Cobalt/validation/cep4/packages/jenkins-reqs.test new file mode 100755 index 0000000000000000000000000000000000000000..d52bcd6af1ea5717422c9df461f5afa423e0b8d9 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/packages/jenkins-reqs.test @@ -0,0 +1,9 @@ +#!/bin/bash + +# Requirements to do a Jenkins build. Only relevant for head0X +[[ `hostname` =~ "head" ]] || exit 0 + +yum -C list installed | grep "^java-" || exit 1 +yum -C list installed subversion || exit 1 +yum -C list installed cmake || exit 1 +yum -C list installed gcc-c++ || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/qpid/9315-no_auth.test b/SubSystems/Online_Cobalt/validation/cep4/qpid/9315-no_auth.test new file mode 100755 index 0000000000000000000000000000000000000000..7de7af4bf4053f5cf295146de8ca8eb151cf4351 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/qpid/9315-no_auth.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "auth=no" /etc/qpid/qpidd.conf || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/slurm/available.test b/SubSystems/Online_Cobalt/validation/cep4/slurm/available.test new file mode 100755 index 0000000000000000000000000000000000000000..9b7a190d6f5d5be379806278493c0c6683563089 --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/slurm/available.test @@ -0,0 +1,3 @@ +#!/bin/bash + +sinfo || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cep4/slurm/nhc.test b/SubSystems/Online_Cobalt/validation/cep4/slurm/nhc.test new file mode 100755 index 0000000000000000000000000000000000000000..6e448e0654d392861eeb4af1d95607d1f7d5bf0a --- /dev/null +++ b/SubSystems/Online_Cobalt/validation/cep4/slurm/nhc.test @@ -0,0 +1,6 @@ +#!/bin/bash -v + +# Run only on cpu nodes +[[ `hostname` =~ "cpu" ]] || exit 0 + +/usr/sbin/nhc || exit 1 diff --git a/SubSystems/Online_Cobalt/validation/cluster/infiniband/osu_bw.test b/SubSystems/Online_Cobalt/validation/cluster/infiniband/osu_bw.test index c20b136df5c0d79a53984b1beaac2ee575062b4b..8e34035ac134693de25892d35c04f98a5f45d287 100755 --- a/SubSystems/Online_Cobalt/validation/cluster/infiniband/osu_bw.test +++ b/SubSystems/Online_Cobalt/validation/cluster/infiniband/osu_bw.test @@ -46,7 +46,7 @@ for target in $HOSTS do [ "$host" == "$target" ] && continue; echo "$host --> $target" - OUT_FILE=osu_bw.$target.out + OUT_FILE=osu_bw.$host.$target.out COMMAND="$MPIRUN --prefix $OPENMPI_DIR -H $host,$target bash -l -c $OSU_BW" run_command "$COMMAND" > "$OUT_FILE" 2> /dev/null || error "$COMMAND FAILED" diff --git a/SubSystems/RAServices/CMakeLists.txt b/SubSystems/RAServices/CMakeLists.txt index 19156d9b5f0d66b3642c851502422d6f4814e4b8..26b4cea0e0d41253d0df78574ce10c9d94669e91 100644 --- a/SubSystems/RAServices/CMakeLists.txt +++ b/SubSystems/RAServices/CMakeLists.txt @@ -1,7 +1,8 @@ # $Id: CMakeLists.txt 20934 2012-05-15 09:26:48Z schoenmakers $ lofar_package(RAServices - DEPENDS MoMQueryService + DEPENDS MAC_Services + MoMQueryService OTDBtoRATaskStatusPropagator RATaskSpecifiedService RAtoOTDBTaskSpecificationPropagator @@ -11,7 +12,9 @@ lofar_package(RAServices ResourceAssignmentEstimator ResourceAssignmentService SystemStatusDatabase - SystemStatusService) + SystemStatusService + DataManagement + RAScripts) # supervisord config files install(FILES diff --git a/SubSystems/RAServices/RAServices.ini b/SubSystems/RAServices/RAServices.ini index 9c678c5c32cff60b56dd77f7db519ec0cfd40596..1d296900b094171536928fb01f2e3c368285737a 100644 --- a/SubSystems/RAServices/RAServices.ini +++ b/SubSystems/RAServices/RAServices.ini @@ -1,3 +1,6 @@ [group:RA_Services] programs=raewebservice,radbservice,radbpglistener,resourceassigner,RATaskSpecified,raestimatorservice,rotspservice,ortspropagator priority=200 + +[group:MAC] +programs=PipelineControl diff --git a/lofarinit.csh.in b/lofarinit.csh.in index dd7a0dadd0011625d241df8db5157f14bace9a14..d8fe0888d0a3781b43d8b321dd7183b5313cbd4c 100644 --- a/lofarinit.csh.in +++ b/lofarinit.csh.in @@ -96,9 +96,9 @@ else # Add the path to the standard paths. if (! $?PATH) then - setenv PATH $LOFARROOT/bin:$LOFARROOT/sbin + setenv PATH $LOFARROOT/sbin:$LOFARROOT/bin else - setenv PATH $LOFARROOT/bin:$LOFARROOT/sbin:$PATH + setenv PATH $LOFARROOT/sbin:$LOFARROOT/bin:$PATH endif if (! $?LD_LIBRARY_PATH) then setenv LD_LIBRARY_PATH $LOFARROOT/$lfr_libdir diff --git a/lofarinit.sh.in b/lofarinit.sh.in index c6d913ac88f39241aa672dbdd03139d7a6f9ea5e..65e797a48fce6f44e41b54ec1f40fcd2f1262508 100644 --- a/lofarinit.sh.in +++ b/lofarinit.sh.in @@ -95,9 +95,9 @@ if [ "$LOFARROOT" = "" -o ! -d $LOFARROOT ]; then else # Add the path to the standard paths. if [ "$PATH" = "" ]; then - PATH=$LOFARROOT/bin:$LOFARROOT/sbin + PATH=$LOFARROOT/sbin:$LOFARROOT/bin else - PATH=$LOFARROOT/bin:$LOFARROOT/sbin:$PATH + PATH=$LOFARROOT/sbin:$LOFARROOT/bin:$PATH fi export PATH if [ "$LD_LIBRARY_PATH" = "" ]; then