diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt index 85af4b367..00ca61d16 100644 --- a/kstars/CMakeLists.txt +++ b/kstars/CMakeLists.txt @@ -1,1181 +1,1190 @@ add_subdirectory( data ) add_subdirectory( icons ) add_subdirectory( htmesh ) if (${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) SET(HAVE_KF5WIT 1) # if(NOT BUILD_KSTARS_LITE) # add_subdirectory( tools/whatsinteresting/qml) # endif(NOT BUILD_KSTARS_LITE) else() SET(HAVE_KF5WIT 0) endif() if (ANDROID AND CMAKE_TOOLCHAIN_FILE) include(${CMAKE_TOOLCHAIN_FILE}) endif () if (NOT ANDROID) find_package(ZLIB REQUIRED) find_package(Threads REQUIRED) endif () if(MSVC) add_definitions(-D_USE_MATH_DEFINES=1) add_definitions(-DNOMINMAX) endif() include_directories( ${kstars_SOURCE_DIR}/kstars ${kstars_SOURCE_DIR}/kstars/skyobjects ${kstars_SOURCE_DIR}/kstars/skycomponents ${kstars_SOURCE_DIR}/kstars/auxiliary ${kstars_SOURCE_DIR}/kstars/time ${kstars_SOURCE_DIR}/kstars/tools ) if (INDI_FOUND) if(BUILD_KSTARS_LITE) set (fits_klite_SRCS fitsviewer/fitsdata.cpp ) set (fits2_klite_SRCS fitsviewer/bayer.c fitsviewer/fpack.c fitsviewer/fpackutil.c ) include_directories(${CFITSIO_INCLUDE_DIR}) include_directories(${NOVA_INCLUDE_DIR}) set (indi_klite_SRCS indi/clientmanagerlite.cpp indi/inditelescopelite.cpp kstarslite/skyitems/skynodes/crosshairnode.cpp kstarslite/skyitems/telescopesymbolsitem.cpp ) endif () set(indiui_SRCS indi/streamform.ui indi/drivermanager.ui indi/opsindi.ui indi/indihostconf.ui indi/customdrivers.ui #indi/telescopewizard.ui ) set(indi_SRCS indi/drivermanager.cpp indi/servermanager.cpp indi/clientmanager.cpp indi/blobmanager.cpp indi/guimanager.cpp indi/driverinfo.cpp indi/deviceinfo.cpp indi/indidevice.cpp indi/indigroup.cpp indi/indiproperty.cpp indi/indielement.cpp indi/indistd.cpp indi/indilistener.cpp indi/inditelescope.cpp indi/indiccd.cpp indi/wsmedia.cpp indi/indifocuser.cpp indi/indifilter.cpp indi/indidome.cpp indi/indiweather.cpp indi/indicap.cpp indi/indilightbox.cpp indi/indidbus.cpp indi/opsindi.cpp #indi/telescopewizardprocess.cpp indi/streamwg.cpp indi/videowg.cpp indi/indiwebmanager.cpp indi/customdrivers.cpp ) if (CFITSIO_FOUND) set(ekosui_SRCS ekos/opsekos.ui ekos/manager.ui ekos/profileeditor.ui ekos/profilewizard.ui # Scheduler ekos/scheduler/scheduler.ui ekos/scheduler/mosaic.ui # Capture ekos/capture/capture.ui ekos/capture/calibrationoptions.ui ekos/capture/dslrinfo.ui ekos/capture/rotatorsettings.ui ekos/capture/customproperties.ui # Align ekos/align/align.ui ekos/align/opsastrometry.ui ekos/align/opsalign.ui ekos/align/opsastrometrycfg.ui ekos/align/opsastrometryindexfiles.ui ekos/align/mountmodel.ui # Focus ekos/focus/focus.ui # Mount ekos/mount/mount.ui # Guide ekos/guide/guide.ui ekos/guide/opscalibration.ui ekos/guide/opsguide.ui ekos/guide/manualdither.ui + ekos/observatory/observatory.ui #TODO remove from GIT #ekos/guide/guider.ui #ekos/guide/rcalibration.ui # Auxiliary ekos/auxiliary/filtersettings.ui ekos/auxiliary/opslogs.ui ekos/auxiliary/serialportassistant.ui # Ekos Live ekos/ekoslive/ekoslivedialog.ui ) set(ekos_SRCS ekos/ekos.cpp ekos/manager.cpp ekos/profileeditor.cpp ekos/profilewizard.cpp ekos/qMDNS.cpp ekos/opsekos.cpp # Auxiliary ekos/auxiliary/dome.cpp ekos/auxiliary/weather.cpp ekos/auxiliary/dustcap.cpp ekos/auxiliary/darklibrary.cpp ekos/auxiliary/filtermanager.cpp ekos/auxiliary/filterdelegate.cpp ekos/auxiliary/opslogs.cpp ekos/auxiliary/serialportassistant.cpp # Capture ekos/capture/capture.cpp ekos/capture/sequencejob.cpp ekos/capture/dslrinfodialog.cpp ekos/capture/rotatorsettings.cpp ekos/capture/customproperties.cpp # Scheduler ekos/scheduler/schedulerjob.cpp ekos/scheduler/scheduler.cpp ekos/scheduler/mosaic.cpp # Focus ekos/focus/focus.cpp # Mount ekos/mount/mount.cpp # Align ekos/align/align.cpp ekos/align/alignview.cpp ekos/align/astrometryparser.cpp ekos/align/opsastrometry.cpp ekos/align/opsalign.cpp ekos/align/opsastrometrycfg.cpp ekos/align/opsastrometryindexfiles.cpp ekos/align/offlineastrometryparser.cpp ekos/align/onlineastrometryparser.cpp ekos/align/remoteastrometryparser.cpp # Guide ekos/guide/guide.cpp ekos/guide/guideinterface.cpp ekos/guide/opscalibration.cpp ekos/guide/opsguide.cpp # Internal Guide ekos/guide/internalguide/gmath.cpp ekos/guide/internalguide/internalguider.cpp #ekos/guide/internalguide/guider.cpp ekos/guide/internalguide/matr.cpp #ekos/guide/internalguide/rcalibration.cpp ekos/guide/internalguide/vect.cpp ekos/guide/internalguide/imageautoguiding.cpp # External Guide ekos/guide/externalguide/phd2.cpp ekos/guide/externalguide/linguider.cpp + #Observatory + ekos/observatory/observatory.cpp + ekos/observatory/observatorymodel.cpp + ekos/observatory/observatorydomemodel.cpp + ekos/observatory/observatoryweathermodel.cpp + # Ekos Live ekos/ekoslive/ekosliveclient.cpp ekos/ekoslive/message.cpp ekos/ekoslive/media.cpp ekos/ekoslive/cloud.cpp ) endif(CFITSIO_FOUND) include_directories(${INDI_INCLUDE_DIR}) endif (INDI_FOUND) if (CFITSIO_FOUND) set (sep_SRCS fitsviewer/sep/analyse.c fitsviewer/sep/aperture.c fitsviewer/sep/background.c fitsviewer/sep/convolve.c fitsviewer/sep/deblend.c fitsviewer/sep/extract.c fitsviewer/sep/lutz.c fitsviewer/sep/util.c ) set (fits_SRCS fitsviewer/fitslabel.cpp fitsviewer/fitsviewer.cpp fitsviewer/fitstab.cpp fitsviewer/fitsdebayer.cpp fitsviewer/opsfits.cpp ) if (Qt5DataVisualization_FOUND) set(fits_SRCS ${fits_SRCS} fitsviewer/starprofileviewer.cpp) endif() set (fits2_SRCS fitsviewer/bayer.c fitsviewer/fpack.c fitsviewer/fpackutil.c fitsviewer/fitshistogram.cpp fitsviewer/fitsview.cpp fitsviewer/fitsdata.cpp ) set (fitsui_SRCS fitsviewer/fitsheaderdialog.ui fitsviewer/statform.ui fitsviewer/fitsdebayer.ui indi/streamform.ui indi/recordingoptions.ui fitsviewer/fitshistogramui.ui fitsviewer/opsfits.ui ) include_directories(${CFITSIO_INCLUDE_DIR}) endif(CFITSIO_FOUND) IF (CFITSIO_FOUND) IF (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")) IF (SANITIZERS) SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -fno-sanitize=address,undefined -fomit-frame-pointer") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsdata.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitshistogram.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsview.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") ELSE () SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") ENDIF () SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/analyse.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/aperture.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-pointer-arith") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/background.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/deblend.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-incompatible-pointer-types-discards-qualifiers") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/extract.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/lutz.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/util.c PROPERTIES COMPILE_FLAGS "-Wno-incompatible-pointer-types-discards-qualifiers") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fpack.c PROPERTIES COMPILE_FLAGS "-Wno-error") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fpackutil.c PROPERTIES COMPILE_FLAGS "-Wno-error") ELSEIF (NOT WIN32) SET_SOURCE_FILES_PROPERTIES(fitsviewer/fpack.c PROPERTIES COMPILE_FLAGS "-Wno-error") SET_SOURCE_FILES_PROPERTIES(fitsviewer/fpackutil.c PROPERTIES COMPILE_FLAGS "-Wno-error") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/aperture.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-arith") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/deblend.c PROPERTIES COMPILE_FLAGS "-Wno-discarded-qualifiers") SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/util.c PROPERTIES COMPILE_FLAGS "-Wno-discarded-qualifiers") ENDIF () ENDIF () if(WCSLIB_FOUND) include_directories( ${WCSLIB_INCLUDE_DIR} ) endif(WCSLIB_FOUND) set(xplanet_SRCS xplanet/opsxplanet.cpp ) set(xplanetui_SRCS xplanet/opsxplanet.ui ) ########### next target ############### set(libkstarstools_SRCS tools/altvstime.cpp tools/avtplotwidget.cpp tools/calendarwidget.cpp tools/conjunctions.cpp tools/eclipsetool.cpp tools/eclipsehandler.cpp tools/eclipsetool/lunareclipsehandler.cpp # tools/jmoontool.cpp tools/approachsolver.cpp tools/ksconjunct.cpp tools/eqplotwidget.cpp tools/astrocalc.cpp tools/modcalcangdist.cpp tools/modcalcapcoord.cpp tools/modcalcaltaz.cpp tools/modcalcdaylength.cpp tools/modcalceclipticcoords.cpp tools/modcalcvizequinox.cpp tools/modcalcgalcoord.cpp tools/modcalcgeodcoord.cpp tools/modcalcjd.cpp tools/modcalcplanets.cpp tools/modcalcsidtime.cpp tools/modcalcvlsr.cpp tools/observinglist.cpp tools/obslistpopupmenu.cpp tools/sessionsortfilterproxymodel.cpp tools/obslistwizard.cpp tools/planetviewer.cpp tools/pvplotwidget.cpp tools/scriptargwidgets.cpp tools/scriptbuilder.cpp tools/scriptfunction.cpp tools/skycalendar.cpp tools/wutdialog.cpp tools/flagmanager.cpp tools/horizonmanager.cpp tools/nameresolver.cpp tools/polarishourangle.cpp #FIXME Port to KF5 #tools/moonphasetool.cpp tools/starhopper.cpp tools/eyepiecefield.cpp tools/exporteyepieceview.cpp tools/starhopperdialog.cpp tools/adddeepskyobject.cpp ) if(${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) set(libkstarstools_SRCS ${libkstarstools_SRCS} tools/whatsinteresting/skyobjlistmodel.cpp tools/whatsinteresting/wiview.cpp tools/whatsinteresting/modelmanager.cpp tools/whatsinteresting/skyobjitem.cpp tools/whatsinteresting/wilpsettings.cpp tools/whatsinteresting/wiequipsettings.cpp tools/whatsinteresting/obsconditions.cpp tools/whatsinteresting/skyobjdescription.cpp ) endif() ki18n_wrap_ui(libkstarstools_ui_SRCS tools/altvstime.ui tools/argchangeviewoption.ui tools/argexportimage.ui tools/argloadcolorscheme.ui tools/arglooktoward.ui tools/argfindobject.ui tools/argprintimage.ui tools/argsetaltaz.ui tools/argsetcolor.ui tools/argsetgeolocation.ui tools/argsetlocaltime.ui tools/argsetradec.ui tools/argsettrack.ui tools/argtimescale.ui tools/argwaitfor.ui tools/argwaitforkey.ui tools/argzoom.ui tools/conjunctions.ui tools/eclipsetool.ui tools/modcalcangdist.ui tools/modcalcapcoord.ui tools/modcalcaltaz.ui tools/modcalcdaylength.ui tools/modcalceclipticcoords.ui tools/modcalcvizequinox.ui tools/modcalcgalcoord.ui tools/modcalcgeod.ui tools/modcalcjd.ui tools/modcalcplanets.ui tools/modcalcsidtime.ui tools/modcalcvlsr.ui tools/observinglist.ui tools/obslistwizard.ui tools/optionstreeview.ui tools/planetviewer.ui tools/scriptbuilder.ui tools/scriptnamedialog.ui tools/skycalendar.ui tools/wutdialog.ui tools/flagmanager.ui tools/starhopperdialog.ui tools/horizonmanager.ui tools/adddeepskyobject.ui tools/polarishourangle.ui ) if (${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) ki18n_wrap_ui(libkstarstools_ui_SRCS tools/whatsinteresting/wilpsettings.ui tools/whatsinteresting/wiequipsettings.ui ) endif() set(libkstarswidgets_SRCS widgets/clicklabel.cpp widgets/dmsbox.cpp widgets/draglistbox.cpp widgets/fovwidget.cpp widgets/logedit.cpp widgets/magnitudespinbox.cpp widgets/mapcanvas.cpp widgets/thumbimage.cpp widgets/timespinbox.cpp widgets/timestepbox.cpp widgets/timeunitbox.cpp widgets/infoboxwidget.cpp # widgets/genericcalendarwidget.cpp # widgets/moonphasecalendarwidget.cpp widgets/kshelplabel.cpp widgets/unitspinboxwidget.cpp ) ki18n_wrap_ui(libkstarswidgets_ui_SRCS # widgets/genericcalendarwidget.ui widgets/unitspinboxwidget.ui ) set(kstars_KCFG_SRCS Options.kcfgc) set(kstars_options_SRCS options/opsadvanced.cpp options/opscatalog.cpp options/opscolors.cpp options/opsguides.cpp options/opssolarsystem.cpp options/opssatellites.cpp options/opssupernovae.cpp ) set(kstars_optionsui_SRCS options/opsadvanced.ui options/opscatalog.ui options/opscolors.ui options/opsguides.ui options/opssolarsystem.ui options/opssatellites.ui options/opssupernovae.ui ) set(kstars_dialogs_SRCS dialogs/addcatdialog.cpp dialogs/addlinkdialog.cpp dialogs/detaildialog.cpp dialogs/finddialog.cpp dialogs/focusdialog.cpp dialogs/fovdialog.cpp dialogs/locationdialog.cpp dialogs/timedialog.cpp dialogs/exportimagedialog.cpp ) set(kstars_dialogsui_SRCS dialogs/addcatdialog.ui dialogs/addlinkdialog.ui dialogs/details_database.ui dialogs/details_data.ui dialogs/details_data_comet.ui dialogs/details_links.ui dialogs/details_log.ui dialogs/details_position.ui dialogs/finddialog.ui dialogs/focusdialog.ui dialogs/fovdialog.ui dialogs/locationdialog.ui dialogs/wizwelcome.ui dialogs/wizlocation.ui dialogs/wizdownload.ui dialogs/wizdata.ui dialogs/newfov.ui dialogs/exportimagedialog.ui ) set(hips_SRCS hips/healpix.cpp hips/hipsrenderer.cpp hips/scanrender.cpp hips/pixcache.cpp hips/urlfiledownload.cpp hips/opships.cpp ) set(hips_manager_SRCS hips/hipsmanager.cpp ) set(oal_SRCS oal/log.cpp oal/observer.cpp oal/site.cpp oal/session.cpp oal/scope.cpp oal/eyepiece.cpp oal/filter.cpp oal/observation.cpp oal/lens.cpp oal/equipmentwriter.cpp oal/observeradd.cpp oal/execute.cpp ) set(printing_SRCS printing/detailstable.cpp printing/finderchart.cpp printing/foveditordialog.cpp printing/fovsnapshot.cpp printing/kstarsdocument.cpp printing/legend.cpp printing/loggingform.cpp printing/printingwizard.cpp printing/pwizchartconfig.cpp printing/pwizchartcontents.cpp printing/pwizfovbrowse.cpp printing/pwizfovconfig.cpp printing/pwizfovmanual.cpp printing/pwizfovsh.cpp printing/pwizfovtypeselection.cpp printing/pwizobjectselection.cpp printing/pwizprint.cpp printing/shfovexporter.cpp printing/simplefovexporter.cpp ) set(printingui_SRCS printing/foveditordialog.ui printing/pwizchartconfig.ui printing/pwizchartcontents.ui printing/pwizfovbrowse.ui printing/pwizfovconfig.ui printing/pwizfovmanual.ui printing/pwizfovsh.ui printing/pwizfovtypeselection.ui printing/pwizobjectselection.ui printing/pwizprint.ui printing/pwizwelcome.ui ) set( kstars_KCFG_SRCS Options.kcfgc ) set(libkstarscomponents_SRCS skycomponents/skylabeler.cpp skycomponents/highpmstarlist.cpp skycomponents/skymapcomposite.cpp skycomponents/skymesh.cpp skycomponents/linelistindex.cpp skycomponents/linelistlabel.cpp skycomponents/noprecessindex.cpp skycomponents/listcomponent.cpp skycomponents/pointlistcomponent.cpp skycomponents/solarsystemsinglecomponent.cpp skycomponents/solarsystemlistcomponent.cpp skycomponents/earthshadowcomponent.cpp skycomponents/asteroidscomponent.cpp skycomponents/cometscomponent.cpp skycomponents/planetmoonscomponent.cpp skycomponents/solarsystemcomposite.cpp skycomponents/satellitescomponent.cpp skycomponents/starcomponent.cpp skycomponents/deepstarcomponent.cpp skycomponents/deepskycomponent.cpp skycomponents/catalogcomponent.cpp skycomponents/syncedcatalogcomponent.cpp skycomponents/constellationartcomponent.cpp skycomponents/constellationboundarylines.cpp skycomponents/constellationlines.cpp skycomponents/constellationnamescomponent.cpp skycomponents/supernovaecomponent.cpp skycomponents/coordinategrid.cpp skycomponents/equatorialcoordinategrid.cpp skycomponents/horizontalcoordinategrid.cpp skycomponents/localmeridiancomponent.cpp skycomponents/ecliptic.cpp skycomponents/equator.cpp skycomponents/artificialhorizoncomponent.cpp skycomponents/hipscomponent.cpp skycomponents/horizoncomponent.cpp skycomponents/milkyway.cpp skycomponents/skycomponent.cpp skycomponents/skycomposite.cpp skycomponents/starblock.cpp skycomponents/starblocklist.cpp skycomponents/starblockfactory.cpp skycomponents/culturelist.cpp skycomponents/flagcomponent.cpp skycomponents/targetlistcomponent.cpp ) #LIST(APPEND libkstarscomponents_SRCS # #skycomponents/notifyupdatesui.cpp # ) IF (BUILD_KSTARS_LITE) set(libkstarstools_ui_klite_SRCS tools/nameresolver.cpp ) ENDIF () set(kstars_skyobjects_SRCS skyobjects/constellationsart.cpp skyobjects/deepskyobject.cpp # skyobjects/jupitermoons.cpp skyobjects/planetmoons.cpp skyobjects/ksasteroid.cpp skyobjects/kscomet.cpp skyobjects/ksmoon.cpp skyobjects/ksearthshadow.cpp skyobjects/ksplanetbase.cpp skyobjects/ksplanet.cpp #skyobjects/kspluto.cpp skyobjects/kssun.cpp skyobjects/skyline.cpp skyobjects/skyobject.cpp skyobjects/skypoint.cpp skyobjects/starobject.cpp skyobjects/trailobject.cpp skyobjects/satellite.cpp skyobjects/satellitegroup.cpp skyobjects/supernova.cpp ) set(kstars_projection_SRCS projections/projector.cpp projections/lambertprojector.cpp projections/gnomonicprojector.cpp projections/stereographicprojector.cpp projections/orthographicprojector.cpp projections/azimuthalequidistantprojector.cpp projections/equirectangularprojector.cpp ) set(kstars_extra_SRCS auxiliary/colorscheme.cpp auxiliary/dms.cpp auxiliary/cachingdms.cpp auxiliary/geolocation.cpp auxiliary/ksfilereader.cpp auxiliary/ksuserdb.cpp auxiliary/binfilehelper.cpp auxiliary/ksutils.cpp auxiliary/ksdssimage.cpp auxiliary/ksdssdownloader.cpp auxiliary/nonlineardoublespinbox.cpp auxiliary/profileinfo.cpp auxiliary/filedownloader.cpp auxiliary/kspaths.cpp auxiliary/QRoundProgressBar.cpp auxiliary/skyobjectlistmodel.cpp auxiliary/ksnotification.cpp auxiliary/QProgressIndicator.cpp auxiliary/ctkrangeslider.cpp time/simclock.cpp time/kstarsdatetime.cpp time/timezonerule.cpp ksnumbers.cpp kstarsdata.cpp texturemanager.cpp #to minimize number of indef KSTARS_LITE skypainter.cpp ) SET(kstars_extra_kstars_SRCS auxiliary/thememanager.cpp auxiliary/schememanager.cpp auxiliary/imageviewer.cpp auxiliary/xplanetimageviewer.cpp auxiliary/fov.cpp auxiliary/thumbnailpicker.cpp auxiliary/thumbnaileditor.cpp auxiliary/imageexporter.cpp auxiliary/kswizard.cpp auxiliary/qcustomplot.cpp kstarsdbus.cpp kspopupmenu.cpp ksalmanac.cpp kstarsactions.cpp kstarsinit.cpp kstars.cpp kstarssplash.cpp skymap.cpp skymapdrawabstract.cpp skymapqdraw.cpp skymapevents.cpp skyqpainter.cpp ) # Temporary solution to allow use of qml files from source dir DELETE SET(KSTARSLITE_CPP_OPTIONS -DSOURCE_DIR=\"${kstars_SOURCE_DIR}\" -DQML_IMPORT="${CMAKE_CURRENT_SOURCE_DIR}") set(klite_SRCS kstarslite.cpp kstarsliteinit.cpp skymaplite.cpp skymapliteevents.cpp #Wrappers kstarslite/skypointlite.cpp kstarslite/skyobjectlite.cpp #ImageProvider kstarslite/imageprovider.cpp #Dialogs kstarslite/dialogs/detaildialoglite.cpp kstarslite/dialogs/finddialoglite.cpp kstarslite/dialogs/locationdialoglite.cpp #RootNode kstarslite/skyitems/rootnode.cpp kstarslite/skyitems/skyopacitynode.cpp kstarslite/skyitems/typedeflite.h #SkyItems kstarslite/skyitems/skyitem.cpp kstarslite/skyitems/planetsitem.cpp kstarslite/skyitems/asteroidsitem.cpp kstarslite/skyitems/cometsitem.cpp kstarslite/skyitems/horizonitem.cpp kstarslite/skyitems/labelsitem.cpp kstarslite/skyitems/constellationnamesitem.cpp kstarslite/skyitems/staritem.cpp kstarslite/skyitems/deepstaritem.cpp kstarslite/skyitems/deepskyitem.cpp kstarslite/skyitems/constellationartitem.cpp kstarslite/skyitems/satellitesitem.cpp kstarslite/skyitems/supernovaeitem.cpp kstarslite/skyitems/fovitem.cpp kstarslite/skyitems/syncedcatalogitem.cpp #Line kstarslite/skyitems/lines/linesitem.cpp kstarslite/skyitems/lines/equatoritem.cpp kstarslite/skyitems/lines/eclipticitem.cpp kstarslite/skyitems/lines/milkywayitem.cpp #SkyNodes kstarslite/skyitems/skynodes/planetnode.cpp kstarslite/skyitems/skynodes/skynode.cpp kstarslite/skyitems/skynodes/pointsourcenode.cpp kstarslite/skyitems/skynodes/planetmoonsnode.cpp kstarslite/skyitems/skynodes/horizonnode.cpp kstarslite/skyitems/skynodes/labelnode.cpp kstarslite/skyitems/skynodes/guidelabelnode.cpp kstarslite/skyitems/skynodes/deepskynode.cpp kstarslite/skyitems/skynodes/dsosymbolnode.cpp kstarslite/skyitems/skynodes/skypolygonnode.cpp kstarslite/skyitems/skynodes/constellationartnode.cpp kstarslite/skyitems/skynodes/satellitenode.cpp kstarslite/skyitems/skynodes/supernovanode.cpp kstarslite/skyitems/skynodes/trixelnode.cpp kstarslite/skyitems/skynodes/fovsymbolnode.cpp #Nodes kstarslite/skyitems/skynodes/nodes/pointnode.cpp kstarslite/skyitems/skynodes/nodes/polynode.cpp kstarslite/skyitems/skynodes/nodes/linenode.cpp kstarslite/skyitems/skynodes/nodes/ellipsenode.cpp kstarslite/skyitems/skynodes/nodes/rectnode.cpp #Other kstarslite/deviceorientation.cpp ) set(kstarslite_libtess_SRC #libtess libtess/gluos.h libtess/priorityq-sort.h libtess/sweep.c libtess/tessmono.c libtess/dict-list.h libtess/glu.h libtess/tessellate.c libtess/dict.c libtess/geom.c libtess/memalloc.c libtess/mesh.c libtess/normal.c libtess/priorityq.c libtess/priorityq-heap.c libtess/render.c libtess/tess.c ) IF (BUILD_KSTARS_LITE) ADD_CUSTOM_TARGET(convert_translations ${CMAKE_SOURCE_DIR}/tools/convert_translations.sh ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) ADD_DEPENDENCIES(convert_translations fetch-translations) IF (ANDROID) ADD_CUSTOM_TARGET(convert_translations_to_android ${CMAKE_SOURCE_DIR}/tools/convert_translations.sh ${CMAKE_BINARY_DIR}/packaging/android/export/share/kstars WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) ADD_DEPENDENCIES(convert_translations_to_android fetch-translations) ENDIF () ENDIF () IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") SET_SOURCE_FILES_PROPERTIES(${kstarslite_libtess_SRC} PROPERTIES COMPILE_FLAGS "-Wno-error") ENDIF () #Qml files will be probably moved to user's data dir, but for use #with QtCreator it is more convenient to have them here set(kstarsliteqml_SRCS kstarslite/qml/main.qml kstarslite/qml/constants/Constants.qml kstarslite/qml/modules/SkyMapLiteWrapper.qml kstarslite/qml/modules/BottomMenu.qml kstarslite/qml/modules/KSPage.qml kstarslite/qml/modules/KSListView.qml kstarslite/qml/modules/KSLabel.qml kstarslite/qml/modules/KSText.qml kstarslite/qml/modules/KSTabButton.qml kstarslite/qml/modules/KSTab.qml kstarslite/qml/modules/KSTabBarArrow.qml kstarslite/qml/modules/KSTextField.qml kstarslite/qml/modules/KSButton.qml kstarslite/qml/modules/TopMenu.qml kstarslite/qml/modules/helpers/TopMenuButton.qml kstarslite/qml/modules/helpers/BottomMenuButton.qml kstarslite/qml/modules/Splash.qml kstarslite/qml/modules/helpers/TimeSpinBox.qml kstarslite/qml/modules/TimePage.qml #Popups kstarslite/qml/modules/popups/ProjectionsPopup.qml kstarslite/qml/modules/popups/FOVPopup.qml kstarslite/qml/modules/popups/ColorSchemePopup.qml #Menus kstarslite/qml/modules/menus/ContextMenu.qml #Helpers kstarslite/qml/modules/helpers/PassiveNotification.qml kstarslite/qml/modules/helpers/KSMenuItem.qml kstarslite/qml/modules/helpers/TelescopeControl.qml #Dialogs kstarslite/qml/dialogs/FindDialog.qml kstarslite/qml/dialogs/LocationDialog.qml kstarslite/qml/dialogs/DetailsDialog.qml kstarslite/qml/dialogs/AboutDialog.qml kstarslite/qml/dialogs/helpers/DetailsItem.qml kstarslite/qml/dialogs/helpers/DetailsAddLink.qml kstarslite/qml/dialogs/helpers/LocationEdit.qml kstarslite/qml/dialogs/helpers/LocationLoading.qml kstarslite/qml/dialogs/menus/DetailsLinkMenu.qml kstarslite/qml/dialogs/menus/LocationsGeoMenu.qml #INDI kstarslite/qml/indi/INDIControlPanel.qml kstarslite/qml/indi/DevicePanel.qml kstarslite/qml/indi/ImagePreview.qml kstarslite/qml/indi/modules/MotionControl.qml kstarslite/qml/indi/modules/Led.qml kstarslite/qml/indi/modules/KSLed.qml kstarslite/qml/indi/modules/Property.qml kstarslite/qml/indi/modules/KSComboBox.qml kstarslite/qml/indi/modules/KSButtonSwitch.qml kstarslite/qml/indi/modules/KSCheckBox.qml kstarslite/qml/indi/modules/KSINDIText.qml kstarslite/qml/indi/modules/KSINDITextField.qml kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml #Tutorial kstarslite/qml/modules/tutorial/TutorialPopup.qml kstarslite/qml/modules/tutorial/TutorialExitPopup.qml kstarslite/qml/modules/tutorial/TutorialStep1.qml kstarslite/qml/modules/tutorial/TutorialStep2.qml kstarslite/qml/modules/tutorial/TutorialStep3.qml kstarslite/qml/modules/tutorial/TutorialStep4.qml kstarslite/qml/modules/tutorial/TutorialStep5.qml kstarslite/qml/modules/tutorial/TutorialPane.qml ) add_subdirectory(kstarslite/qml) ADD_CUSTOM_TARGET(kstarsliteqml SOURCES ${kstarsliteqml_SRCS}) if(ANDROID) add_subdirectory(kstarslite/res) endif(ANDROID) set(kstars_SRCS ${indi_SRCS} ${fits_SRCS} ${ekos_SRCS} ${libkstarswidgets_SRCS} ${libkstarscomponents_SRCS} ${libkstarstools_SRCS} ${kstars_extra_SRCS} ${kstars_extra_kstars_SRCS} ${kstars_projection_SRCS} ${xplanet_SRCS} ${kstars_options_SRCS} ${kstars_skyobjects_SRCS} ${kstars_dialogs_SRCS} ${hips_SRCS} ${oal_SRCS} ${printing_SRCS} #KStars Lite ${kstarslite_SRCS} # Generated files ${libkstarstools_ui_SRCS} ${libkstarswidgets_ui_SRCS} ) set(kstarslite_SRCS ${indi_klite_SRCS} ${libkstarscomponents_SRCS} ${kstars_extra_SRCS} ${kstars_projection_SRCS} ${kstars_skyobjects_SRCS} # KStars Lite sources ${klite_SRCS} # Generated files ${libkstarstools_ui_klite_SRCS} ) # Generate all the necessary QLoggingCategory files ecm_qt_declare_logging_category(kstars_SRCS HEADER kstars_debug.h IDENTIFIER KSTARS CATEGORY_NAME org.kde.kstars) ecm_qt_declare_logging_category(kstars_SRCS HEADER indi_debug.h IDENTIFIER KSTARS_INDI CATEGORY_NAME org.kde.kstars.indi) ecm_qt_declare_logging_category(kstars_SRCS HEADER fits_debug.h IDENTIFIER KSTARS_FITS CATEGORY_NAME org.kde.kstars.fits) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_debug.h IDENTIFIER KSTARS_EKOS CATEGORY_NAME org.kde.kstars.ekos) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_capture_debug.h IDENTIFIER KSTARS_EKOS_CAPTURE CATEGORY_NAME org.kde.kstars.ekos.capture) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_focus_debug.h IDENTIFIER KSTARS_EKOS_FOCUS CATEGORY_NAME org.kde.kstars.ekos.focus) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_align_debug.h IDENTIFIER KSTARS_EKOS_ALIGN CATEGORY_NAME org.kde.kstars.ekos.align) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_guide_debug.h IDENTIFIER KSTARS_EKOS_GUIDE CATEGORY_NAME org.kde.kstars.ekos.guide) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_mount_debug.h IDENTIFIER KSTARS_EKOS_MOUNT CATEGORY_NAME org.kde.kstars.ekos.mount) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_scheduler_debug.h IDENTIFIER KSTARS_EKOS_SCHEDULER CATEGORY_NAME org.kde.kstars.ekos.scheduler) +ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_observatory_debug.h IDENTIFIER KSTARS_EKOS_OBSERVATORY CATEGORY_NAME org.kde.kstars.ekos.observatory) kconfig_add_kcfg_files(kstars_SRCS ${kstars_KCFG_SRCS}) ecm_qt_declare_logging_category(kstarslite_SRCS HEADER kstars_debug.h IDENTIFIER KSTARS CATEGORY_NAME org.kde.kstars) ecm_qt_declare_logging_category(kstarslite_SRCS HEADER fits_debug.h IDENTIFIER KSTARS_FITS CATEGORY_NAME org.kde.kstars.fits) kconfig_add_kcfg_files(kstarslite_SRCS ${kstars_KCFG_SRCS}) IF (UNITY_BUILD) ENABLE_UNITY_BUILD(kstars kstars_SRCS 10 cpp) ENABLE_UNITY_BUILD(kstarslite kstarslite_SRCS 10 cpp) ENDIF () set(kstars_SRCS ${kstars_SRCS} ${fits2_SRCS} ${sep_SRCS} ${hips_manager_SRCS}) set(kstarslite_SRCS ${kstarslite_SRCS} ${fits_klite_SRCS} ${sep_SRCS} ${fits2_klite_SRCS} ${kstarslite_libtess_SRC}) IF (NOT ANDROID) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.xml kstars.h KStars) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.SimClock.xml simclock.h SimClock) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.FOV.xml fov.h FOV) IF (INDI_FOUND) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.INDI.xml indi/indidbus.h INDIDBus) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.xml ekos/manager.h Ekos::Manager) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Capture.xml ekos/capture/capture.h Ekos::Capture) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Focus.xml ekos/focus/focus.h Ekos::Focus) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Guide.xml ekos/guide/guide.h Ekos::Guide) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Align.xml ekos/align/align.h Ekos::Align) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Mount.xml ekos/mount/mount.h Ekos::Mount) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Dome.xml ekos/auxiliary/dome.h Ekos::Dome) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Weather.xml ekos/auxiliary/weather.h Ekos::Weather) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.DustCap.xml ekos/auxiliary/dustcap.h Ekos::DustCap) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Scheduler.xml ekos/scheduler/scheduler.h Ekos::Scheduler) + qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Observatory.xml ekos/observatory/observatory.h Ekos::Observatory) ENDIF () ki18n_wrap_ui(kstars_SRCS ${indiui_SRCS} ${ui_SRCS} ${fitsui_SRCS} ${ekosui_SRCS} ${xplanetui_SRCS} ${kstars_optionsui_SRCS} ${kstars_dialogsui_SRCS} ${printingui_SRCS} auxiliary/thumbnailpicker.ui auxiliary/thumbnaileditor.ui oal/observeradd.ui oal/equipmentwriter.ui oal/execute.ui hips/opships.ui hips/opshipsdisplay.ui hips/opshipscache.ui #skycomponents/notifyupdatesui.ui ) add_library(KStarsLib STATIC ${kstars_SRCS}) target_link_libraries(KStarsLib LibKSDataHandlers htmesh KF5::Crash KF5::I18n KF5::NewStuff KF5::KIOFileWidgets KF5::WidgetsAddons KF5::Plotting KF5::Notifications Qt5::Gui Qt5::PrintSupport Qt5::Sql Qt5::Svg Qt5::Qml Qt5::Quick Qt5::Network #Qt5::Positioning Qt5::Concurrent Qt5::WebSockets ${ZLIB_LIBRARIES} ) if (Qt5Keychain_FOUND) include_directories(KStarsLib PUBLIC ${QTKEYCHAIN_INCLUDE_DIRS}) target_link_libraries(KStarsLib ${QTKEYCHAIN_LIBRARIES}) endif(Qt5Keychain_FOUND) if (Qt5DataVisualization_FOUND) target_link_libraries(KStarsLib Qt5::DataVisualization) endif(Qt5DataVisualization_FOUND) if (KF5NotifyConfig_FOUND) target_link_libraries(KStarsLib KF5::NotifyConfig) endif(KF5NotifyConfig_FOUND) if(NOT WIN32) target_link_libraries(KStarsLib m) endif(NOT WIN32) ENDIF () if (BUILD_KSTARS_LITE) add_library(KStarsLiteLib STATIC ${kstarslite_SRCS}) target_link_libraries(KStarsLiteLib LibKSDataHandlers htmesh KF5::I18n KF5::Plotting KF5::ConfigGui Qt5::Gui Qt5::Sql Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Positioning Qt5::PositioningQuick Qt5::Concurrent ${ZLIB_LIBRARIES} ) if (ANDROID) target_link_libraries(KStarsLiteLib Qt5::AndroidExtras) endif () endif () if (CFITSIO_FOUND) if (NOT ANDROID) target_include_directories(KStarsLib PUBLIC ${CFITSIO_INCLUDE_DIR}) target_link_libraries(KStarsLib ${CFITSIO_LIBRARIES}) endif() if (BUILD_KSTARS_LITE) target_include_directories(KStarsLiteLib PUBLIC ${CFITSIO_INCLUDE_DIR}) target_link_libraries(KStarsLiteLib ${CFITSIO_LIBRARIES}) endif() endif(CFITSIO_FOUND) if(INDI_FOUND) if (NOT ANDROID) find_package(Nova REQUIRED) include_directories(${NOVA_INCLUDE_DIR}) endif () ## Support Multiple Platforms. All Require INDI ## WIN32 Desktop: Requires INDI Qt5 Client + GSL ## WIN32 Lite: Requires INDI Qt5 Client ## Linux + MacOS Desktop: Requires INDI Client + GSL ## Linux + MacOS Lite: Requires INDI Qt5 Client ## Android: Requires INDI Qt5 Client built for Android if (NOT ANDROID) find_package(GSL REQUIRED) include_directories(${GSL_INCLUDE_DIRS}) target_link_libraries(KStarsLib ${GSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} KF5::Notifications) endif () if(WIN32 OR ANDROID) if(ANDROID) target_link_libraries(KStarsLiteLib ${INDI_CLIENT_ANDROID_LIBRARIES} ${CFITSIO_LIBRARIES} ${LIBRAW_LIBRARIES}) target_compile_options(KStarsLiteLib PRIVATE ${KSTARSLITE_CPP_OPTIONS} -DUSE_QT5_INDI -DKSTARS_LITE) else(ANDROID) target_link_libraries(KStarsLib ${INDI_CLIENT_LIBRARIES} ${NOVA_LIBRARIES}) endif(ANDROID) else(WIN32 OR ANDROID) if (BUILD_KSTARS_LITE) target_link_libraries(KStarsLiteLib ${INDI_CLIENT_QT_LIBRARIES} ${NOVA_LIBRARIES} z) target_compile_options(KStarsLiteLib PRIVATE ${KSTARSLITE_CPP_OPTIONS} -DUSE_QT5_INDI -DKSTARS_LITE) endif(BUILD_KSTARS_LITE) target_link_libraries(KStarsLib ${INDI_CLIENT_LIBRARIES} ${NOVA_LIBRARIES} z) endif(WIN32 OR ANDROID) endif(INDI_FOUND) if(WCSLIB_FOUND) target_link_libraries(KStarsLib ${WCSLIB_LIBRARIES}) if (BUILD_KSTARS_LITE) target_link_libraries(KStarsLiteLib ${WCSLIB_LIBRARIES}) endif() endif (WCSLIB_FOUND) if(LibRaw_FOUND) if (NOT ANDROID) target_link_libraries(KStarsLib ${LibRaw_LIBRARIES}) endif() if (BUILD_KSTARS_LITE) target_link_libraries(KStarsLiteLib ${LibRaw_LIBRARIES}) endif() endif (LibRaw_FOUND) #FIXME Enable OpenGL Later #if( OPENGL_FOUND ) # target_link_libraries(KStarsLib # ${OPENGL_LIBRARIES} # ${QT_QTOPENGL_LIBRARY} # ) #endif( OPENGL_FOUND ) set (KSTARS_APP_SRCS main.cpp ) # add icon to application sources ecm_add_app_icon(KSTARS_APP_SRCS ICONS ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kstars.png ) qt5_add_resources(KSTARS_APP_SRCS data/kstars.qrc) if (ANDROID) add_library(kstars SHARED ${KSTARS_APP_SRCS}) target_compile_options(kstars PRIVATE ${KSTARSLITE_CPP_OPTIONS} -DUSE_QT5_INDI -DKSTARS_LITE) add_dependencies(KStarsLiteLib cfitsio indi raw) target_link_libraries(kstars KStarsLiteLib) else () if (BUILD_KSTARS_LITE) add_executable(kstars_lite ${KSTARS_APP_SRCS}) target_compile_options(kstars_lite PRIVATE ${KSTARSLITE_CPP_OPTIONS} -DUSE_QT5_INDI -DKSTARS_LITE) target_link_libraries(kstars_lite KStarsLiteLib) endif() add_executable(kstars ${KSTARS_APP_SRCS}) target_link_libraries(kstars KStarsLib) endif () install(TARGETS kstars ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.kstars.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES kstars.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) install(FILES kstars.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) if(INDI_FOUND) #install(FILES ekos/mount/mountbox.qml DESTINATION ${KDE_INSTALL_DATADIR}/kstars/ekos/mount/qml) #install(DIRECTORY ekos/mount/ DESTINATION ${KDE_INSTALL_DATADIR}/kstars/ekos/mount/qml # FILES_MATCHING PATTERN "*.png") endif() if (NOT ANDROID AND BUILD_KSTARS_LITE) install(TARGETS kstars_lite ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) endif() diff --git a/kstars/data/icons/ekos_observatory.png b/kstars/data/icons/ekos_observatory.png new file mode 100644 index 000000000..55904497b Binary files /dev/null and b/kstars/data/icons/ekos_observatory.png differ diff --git a/kstars/data/kstars.qrc b/kstars/data/kstars.qrc index 14f126d13..5f59ebf7c 100644 --- a/kstars/data/kstars.qrc +++ b/kstars/data/kstars.qrc @@ -1,72 +1,73 @@ icons/ekos_align.png icons/ekos_ccd.png icons/ekos_focus.png icons/ekos_guide.png icons/ekos_mount.png icons/ekos_scheduler.png icons/ekos_setup.png icons/fov.png icons/histogram.png icons/glstarbase.png icons/center_telescope.svg icons/center_telescope_red.svg icons/AlignFailure.svg icons/AlignSuccess.svg icons/AlignWarning.svg icons/astrometry.svg icons/stellarmate.svg icons/center_telescope_magenta.svg icons/dome-park.svg icons/dome-unpark.svg icons/star_profile.svg icons/astrometry-optional.svg icons/astrometry-recommended.svg icons/astrometry-required.svg indidrivers.xml qml/whatisinteresting/CategoryTitle.qml qml/whatisinteresting/DetailsItem.qml qml/whatisinteresting/downloadIcon.png qml/whatisinteresting/favoriteIcon.png qml/whatisinteresting/helpIcon.png qml/whatisinteresting/iconcredits.dat qml/whatisinteresting/inspectIcon.png qml/whatisinteresting/leftArrow.png qml/whatisinteresting/next.png qml/whatisinteresting/previous.png qml/whatisinteresting/reloadIcon.png qml/whatisinteresting/ScrollBar.qml qml/whatisinteresting/settingsIcon.png qml/whatisinteresting/visibleIcon.png qml/whatisinteresting/wiview.qml qml/mount/go-east.png qml/mount/go-north.png qml/mount/go-northeast.png qml/mount/go-northwest.png qml/mount/go-south.png qml/mount/go-southeast.png qml/mount/go-southwest.png qml/mount/go-west.png qml/mount/mountbox.qml qml/mount/stop.png icons/kstars_satellites_invisible.svg icons/kstars_satellites_visible.svg icons/cloud-online.svg + icons/ekos_observatory.png kstars.knsrc kstarsui.rc fitsviewerui.rc noimage.png reticle12.png reticle24.png sm_animation.gif diff --git a/kstars/ekos/auxiliary/dome.cpp b/kstars/ekos/auxiliary/dome.cpp index 653d2bf02..9c9ea8b0a 100644 --- a/kstars/ekos/auxiliary/dome.cpp +++ b/kstars/ekos/auxiliary/dome.cpp @@ -1,159 +1,179 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application 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. */ #include "dome.h" #include "domeadaptor.h" #include "ekos/manager.h" #include "indi/driverinfo.h" #include "indi/clientmanager.h" #include "kstars.h" #include namespace Ekos { Dome::Dome() { new DomeAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Dome", this); currentDome = nullptr; } void Dome::setDome(ISD::GDInterface *newDome) { if (newDome == currentDome) return; currentDome = static_cast(newDome); currentDome->disconnect(this); connect(currentDome, &ISD::Dome::newStatus, this, &Dome::newStatus); connect(currentDome, &ISD::Dome::newParkStatus, this, &Dome::newParkStatus); connect(currentDome, &ISD::Dome::newParkStatus, [&](ISD::ParkStatus status) {m_ParkStatus = status;}); + connect(currentDome, &ISD::Dome::newShutterStatus, this, &Dome::newShutterStatus); + connect(currentDome, &ISD::Dome::newShutterStatus, [&](ISD::Dome::ShutterStatus status) {m_ShutterStatus = status;}); connect(currentDome, &ISD::Dome::azimuthPositionChanged, this, &Dome::azimuthPositionChanged); connect(currentDome, &ISD::Dome::ready, this, &Dome::ready); + connect(currentDome, &ISD::Dome::Disconnected, this, &Dome::disconnected); } void Dome::setTelescope(ISD::GDInterface *newTelescope) { if (currentDome == nullptr) return; ITextVectorProperty *activeDevices = currentDome->getBaseDevice()->getText("ACTIVE_DEVICES"); if (activeDevices) { IText *activeTelescope = IUFindText(activeDevices, "ACTIVE_TELESCOPE"); if (activeTelescope) { IUSaveText(activeTelescope, newTelescope->getDeviceName()); currentDome->getDriverInfo()->getClientManager()->sendNewText(activeDevices); } } } bool Dome::canPark() { if (currentDome == nullptr) return false; return currentDome->canPark(); } bool Dome::park() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->Park(); } bool Dome::unpark() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->UnPark(); } bool Dome::abort() { if (currentDome == nullptr) return false; return currentDome->Abort(); } bool Dome::isMoving() { if (currentDome == nullptr) return false; return currentDome->isMoving(); } bool Dome::canAbsoluteMove() { if (currentDome) return currentDome->canAbsMove(); return false; } double Dome::azimuthPosition() { if (currentDome) return currentDome->azimuthPosition(); return -1; } void Dome::setAzimuthPosition(double position) { if (currentDome) currentDome->setAzimuthPosition(position); } +bool Dome::hasShutter() +{ + if (currentDome) + return currentDome->hasShutter(); + // no dome, no shutter + return false; +} + +bool Dome::controlShutter(bool open) +{ + + if (currentDome) + return currentDome->ControlShutter(open); + // no dome, no shutter control + return false; +} + #if 0 Dome::ParkingStatus Dome::getParkingStatus() { if (currentDome == nullptr || currentDome->canPark() == false) return PARKING_ERROR; ISwitchVectorProperty *parkSP = currentDome->getBaseDevice()->getSwitch("DOME_PARK"); if (parkSP == nullptr) return PARKING_ERROR; switch (parkSP->s) { case IPS_IDLE: return PARKING_IDLE; case IPS_OK: if (parkSP->sp[0].s == ISS_ON) return PARKING_OK; else return UNPARKING_OK; case IPS_BUSY: if (parkSP->sp[0].s == ISS_ON) return PARKING_BUSY; else return UNPARKING_BUSY; case IPS_ALERT: return PARKING_ERROR; } return PARKING_ERROR; } #endif } diff --git a/kstars/ekos/auxiliary/dome.h b/kstars/ekos/auxiliary/dome.h index f922b045b..a89b4de60 100644 --- a/kstars/ekos/auxiliary/dome.h +++ b/kstars/ekos/auxiliary/dome.h @@ -1,123 +1,131 @@ /* Ekos Dome interface Copyright (C) 2015 Jasem Mutlaq This application 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. */ #pragma once #include "indi/indistd.h" #include "indi/indidome.h" #include namespace Ekos { /** * @class Dome * @short Supports basic dome functions * * @author Jasem Mutlaq * @version 1.0 */ class Dome : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Dome") Q_PROPERTY(ISD::Dome::Status status READ status NOTIFY newStatus) Q_PROPERTY(ISD::ParkStatus parkStatus READ parkStatus NOTIFY newParkStatus) Q_PROPERTY(bool canPark READ canPark) Q_PROPERTY(bool canAbsoluteMove READ canAbsoluteMove) Q_PROPERTY(bool isMoving READ isMoving) Q_PROPERTY(double azimuthPosition READ azimuthPosition WRITE setAzimuthPosition NOTIFY azimuthPositionChanged) public: Dome(); virtual ~Dome() override = default; /** * @defgroup DomeDBusInterface Ekos DBus Interface - Dome Interface * Ekos::Dome interface provides advanced basic dome operations. */ /*@{*/ /** * DBUS interface function. * Abort dome */ Q_SCRIPTABLE bool abort(); /** * DBUS interface function. * Can dome park? */ Q_SCRIPTABLE bool canPark(); /** * DBUS interface function. * Can dome move to an absolute azimuth position? */ Q_SCRIPTABLE bool canAbsoluteMove(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool park(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool unpark(); /** * DBUS interface function. * Get the dome park status */ //Q_SCRIPTABLE ParkingStatus getParkingStatus(); /** * DBUS interface function. * Check if the dome is in motion */ Q_SCRIPTABLE bool isMoving(); Q_SCRIPTABLE double azimuthPosition(); Q_SCRIPTABLE void setAzimuthPosition(double position); + Q_SCRIPTABLE bool hasShutter(); + Q_SCRIPTABLE bool controlShutter(bool open); + /** @}*/ /** * @brief setDome set the dome device * @param newDome pointer to Dome device. */ void setDome(ISD::GDInterface *newDome); /** * @brief setTelescope Set the telescope device. This is only used to sync ACTIVE_TELESCOPE to the current active telescope. * @param newTelescope pointer to telescope device. */ void setTelescope(ISD::GDInterface *newTelescope); ISD::Dome::Status status() { return currentDome->status(); } + ISD::Dome::ShutterStatus shutterStatus() { return currentDome->shutterStatus(); } ISD::ParkStatus parkStatus() { return m_ParkStatus; } - signals: +signals: void newStatus(ISD::Dome::Status status); void newParkStatus(ISD::ParkStatus status); + void newShutterStatus(ISD::Dome::ShutterStatus status); void azimuthPositionChanged(double position); void ready(); + // Signal when the underlying ISD::Dome signals a Disconnected() + void disconnected(); private: // Devices needed for Dome operation ISD::Dome *currentDome { nullptr }; ISD::ParkStatus m_ParkStatus { ISD::PARK_UNKNOWN }; + ISD::Dome::ShutterStatus m_ShutterStatus { ISD::Dome::SHUTTER_UNKNOWN }; }; } diff --git a/kstars/ekos/auxiliary/weather.cpp b/kstars/ekos/auxiliary/weather.cpp index d1f05df67..e353b8c65 100644 --- a/kstars/ekos/auxiliary/weather.cpp +++ b/kstars/ekos/auxiliary/weather.cpp @@ -1,55 +1,57 @@ /* Ekos Weather Interface Copyright (C) 2015 Jasem Mutlaq This application 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. */ #include "weather.h" #include "ekos/manager.h" #include "kstars.h" #include "weatheradaptor.h" #include namespace Ekos { Weather::Weather() { new WeatherAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Weather", this); qRegisterMetaType("ISD::Weather::Status"); qDBusRegisterMetaType(); } void Weather::setWeather(ISD::GDInterface *newWeather) { if (newWeather == currentWeather) return; currentWeather = static_cast(newWeather); currentWeather->disconnect(this); connect(currentWeather, &ISD::Weather::newStatus, this, &Weather::newStatus); connect(currentWeather, &ISD::Weather::ready, this, &Weather::ready); + connect(currentWeather, &ISD::Weather::Connected, this, &Weather::ready); + connect(currentWeather, &ISD::Weather::Disconnected, this, &Weather::disconnected); } ISD::Weather::Status Weather::status() { if (currentWeather == nullptr) return ISD::Weather::WEATHER_IDLE; return currentWeather->getWeatherStatus(); } quint16 Weather::getUpdatePeriod() { if (currentWeather == nullptr) return 0; return currentWeather->getUpdatePeriod(); } } diff --git a/kstars/ekos/auxiliary/weather.h b/kstars/ekos/auxiliary/weather.h index 30c38947d..e2d03c795 100644 --- a/kstars/ekos/auxiliary/weather.h +++ b/kstars/ekos/auxiliary/weather.h @@ -1,78 +1,80 @@ /* Ekos Weather interface Copyright (C) 2015 Jasem Mutlaq This application 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. */ #pragma once #include "indi/indistd.h" #include "indi/indiweather.h" #include namespace Ekos { /** * @class Weather * @short Supports basic weather station functions * * @author Jasem Mutlaq * @version 1.0 */ class Weather : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Weather") Q_PROPERTY(ISD::Weather::Status status READ status NOTIFY newStatus) Q_PROPERTY(quint16 updatePeriod READ getUpdatePeriod) public: Weather(); virtual ~Weather() override = default; /** * @defgroup WeatherDBusInterface Ekos DBus Interface - Weather Interface * Ekos::Weather interface provides basic weather operations. */ /*@{*/ /** * DBUS interface function. * Get Weather status. * @return Either IPS_OK for OK acceptable weather, IPS_BUSY for weather in warning zone, and IPS_ALERT for weather in danger zone. The zones ranges are defined by the INDI weather driver. */ Q_SCRIPTABLE ISD::Weather::Status status(); /** * DBUS interface function. * Get Weather Update Period * @return Return weather update period in minute. The weather is updated every X minutes from the weather source. */ Q_SCRIPTABLE quint16 getUpdatePeriod(); /** @}*/ /** * @brief setWeather set the Weather device * @param newWeather pointer to Weather device. */ void setWeather(ISD::GDInterface *newWeather); signals: /** * @brief newStatus Weather Status * @param status IPS_OK --> Good, IPS_BUSY --> Warning, IPS_ALERT --> Alert */ void newStatus(ISD::Weather::Status status); void ready(); + // Signal when the underlying ISD::Weather signals a Disconnected() + void disconnected(); private: // Devices needed for Weather operation ISD::Weather *currentWeather { nullptr }; }; } diff --git a/kstars/ekos/manager.cpp b/kstars/ekos/manager.cpp index cd5157176..c72260cea 100644 --- a/kstars/ekos/manager.cpp +++ b/kstars/ekos/manager.cpp @@ -1,3193 +1,3220 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application 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. */ #include "manager.h" #include "ekosadaptor.h" #include "kstars.h" #include "kstarsdata.h" #include "opsekos.h" #include "Options.h" #include "profileeditor.h" #include "profilewizard.h" #include "skymap.h" #include "auxiliary/darklibrary.h" #include "auxiliary/QProgressIndicator.h" #include "capture/sequencejob.h" #include "fitsviewer/fitstab.h" #include "fitsviewer/fitsview.h" #include "fitsviewer/fitsdata.h" #include "indi/clientmanager.h" #include "indi/driverinfo.h" #include "indi/drivermanager.h" #include "indi/guimanager.h" #include "indi/indielement.h" #include "indi/indilistener.h" #include "indi/indiproperty.h" #include "indi/indiwebmanager.h" #include "ekoslive/ekosliveclient.h" #include "ekoslive/message.h" #include "ekoslive/media.h" #include #include #include #include #include #include #include #define MAX_REMOTE_INDI_TIMEOUT 15000 #define MAX_LOCAL_INDI_TIMEOUT 5000 namespace Ekos { Manager::Manager(QWidget * parent) : QDialog(parent) { #ifdef Q_OS_OSX if (Options::independentWindowEkos()) setWindowFlags(Qt::Window); else { setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(changeAlwaysOnTop(Qt::ApplicationState))); } #else // if (Options::independentWindowEkos()) // setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); #endif setupUi(this); qRegisterMetaType("Ekos::CommunicationStatus"); qDBusRegisterMetaType(); new EkosAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos", this); setWindowIcon(QIcon::fromTheme("kstars_ekos")); profileModel.reset(new QStandardItemModel(0, 4)); profileModel->setHorizontalHeaderLabels(QStringList() << "id" << "name" << "host" << "port"); captureProgress->setValue(0); sequenceProgress->setValue(0); sequenceProgress->setDecimals(0); sequenceProgress->setFormat("%v"); imageProgress->setValue(0); imageProgress->setDecimals(1); imageProgress->setFormat("%v"); imageProgress->setBarStyle(QRoundProgressBar::StyleLine); countdownTimer.setInterval(1000); connect(&countdownTimer, &QTimer::timeout, this, &Ekos::Manager::updateCaptureCountDown); toolsWidget->setIconSize(QSize(48, 48)); connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection); // Enable scheduler Tab toolsWidget->setTabEnabled(1, false); // Start/Stop INDI Server connect(processINDIB, &QPushButton::clicked, this, &Ekos::Manager::processINDI); processINDIB->setIcon(QIcon::fromTheme("media-playback-start")); processINDIB->setToolTip(i18n("Start")); // Connect/Disconnect INDI devices connect(connectB, &QPushButton::clicked, this, &Ekos::Manager::connectDevices); connect(disconnectB, &QPushButton::clicked, this, &Ekos::Manager::disconnectDevices); ekosLiveB->setAttribute(Qt::WA_LayoutUsesWidgetRect); ekosLiveClient.reset(new EkosLive::Client(this)); connect(ekosLiveClient.get(), &EkosLive::Client::connected, [this]() { emit ekosLiveStatusChanged(true); }); connect(ekosLiveClient.get(), &EkosLive::Client::disconnected, [this]() { emit ekosLiveStatusChanged(false); }); // INDI Control Panel //connect(controlPanelB, &QPushButton::clicked, GUIManager::Instance(), SLOT(show())); connect(ekosLiveB, &QPushButton::clicked, [&]() { ekosLiveClient.get()->show(); ekosLiveClient.get()->raise(); }); connect(this, &Manager::ekosStatusChanged, ekosLiveClient.get()->message(), &EkosLive::Message::setEkosStatingStatus); connect(ekosLiveClient.get()->message(), &EkosLive::Message::connected, [&]() { ekosLiveB->setIcon(QIcon(":/icons/cloud-online.svg")); }); connect(ekosLiveClient.get()->message(), &EkosLive::Message::disconnected, [&]() { ekosLiveB->setIcon(QIcon::fromTheme("folder-cloud")); }); connect(ekosLiveClient.get()->media(), &EkosLive::Media::newBoundingRect, ekosLiveClient.get()->message(), &EkosLive::Message::setBoundingRect); connect(ekosLiveClient.get()->message(), &EkosLive::Message::resetPolarView, ekosLiveClient.get()->media(), &EkosLive::Media::resetPolarView); // Serial Port Assistat connect(serialPortAssistantB, &QPushButton::clicked, [&]() { serialPortAssistant->show(); serialPortAssistant->raise(); }); connect(this, &Ekos::Manager::ekosStatusChanged, [&](Ekos::CommunicationStatus status) { indiControlPanelB->setEnabled(status == Ekos::Success); }); connect(indiControlPanelB, &QPushButton::clicked, [&]() { KStars::Instance()->actionCollection()->action("show_control_panel")->trigger(); }); connect(optionsB, &QPushButton::clicked, [&]() { KStars::Instance()->actionCollection()->action("configure")->trigger(); }); // Save as above, but it appears in all modules connect(ekosOptionsB, &QPushButton::clicked, this, &Ekos::Manager::showEkosOptions); // Clear Ekos Log connect(clearB, &QPushButton::clicked, this, &Ekos::Manager::clearLog); // Logs KConfigDialog * dialog = new KConfigDialog(this, "logssettings", Options::self()); opsLogs = new Ekos::OpsLogs(); KPageWidgetItem * page = dialog->addPage(opsLogs, i18n("Logging")); page->setIcon(QIcon::fromTheme("configure")); connect(logsB, &QPushButton::clicked, dialog, &KConfigDialog::show); connect(dialog->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces); connect(dialog->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &Ekos::Manager::updateDebugInterfaces); // Summary // previewPixmap = new QPixmap(QPixmap(":/images/noimage.png")); // Profiles connect(addProfileB, &QPushButton::clicked, this, &Ekos::Manager::addProfile); connect(editProfileB, &QPushButton::clicked, this, &Ekos::Manager::editProfile); connect(deleteProfileB, &QPushButton::clicked, this, &Ekos::Manager::deleteProfile); connect(profileCombo, static_cast(&QComboBox::currentTextChanged), [ = ](const QString & text) { Options::setProfile(text); if (text == "Simulators") { editProfileB->setEnabled(false); deleteProfileB->setEnabled(false); } else { editProfileB->setEnabled(true); deleteProfileB->setEnabled(true); } }); // Ekos Wizard connect(wizardProfileB, &QPushButton::clicked, this, &Ekos::Manager::wizardProfile); addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); // Set Profile icons addProfileB->setIcon(QIcon::fromTheme("list-add")); addProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); editProfileB->setIcon(QIcon::fromTheme("document-edit")); editProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); deleteProfileB->setIcon(QIcon::fromTheme("list-remove")); deleteProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); wizardProfileB->setIcon(QIcon::fromTheme("tools-wizard")); wizardProfileB->setAttribute(Qt::WA_LayoutUsesWidgetRect); customDriversB->setIcon(QIcon::fromTheme("roll")); customDriversB->setAttribute(Qt::WA_LayoutUsesWidgetRect); connect(customDriversB, &QPushButton::clicked, DriverManager::Instance(), &DriverManager::showCustomDrivers); // Load all drivers loadDrivers(); // Load add driver profiles loadProfiles(); // INDI Control Panel and Ekos Options optionsB->setIcon(QIcon::fromTheme("configure", QIcon(":/icons/ekos_setup.png"))); optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect); // Setup Tab toolsWidget->tabBar()->setTabIcon(0, QIcon(":/icons/ekos_setup.png")); toolsWidget->tabBar()->setTabToolTip(0, i18n("Setup")); // Initialize Ekos Scheduler Module schedulerProcess.reset(new Ekos::Scheduler()); toolsWidget->addTab(schedulerProcess.get(), QIcon(":/icons/ekos_scheduler.png"), ""); toolsWidget->tabBar()->setTabToolTip(1, i18n("Scheduler")); connect(schedulerProcess.get(), &Scheduler::newLog, this, &Ekos::Manager::updateLog); //connect(schedulerProcess.get(), SIGNAL(newTarget(QString)), mountTarget, SLOT(setText(QString))); connect(schedulerProcess.get(), &Ekos::Scheduler::newTarget, [&](const QString & target) { mountTarget->setText(target); ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", target}})); }); // Temporary fix. Not sure how to resize Ekos Dialog to fit contents of the various tabs in the QScrollArea which are added // dynamically. I used setMinimumSize() but it doesn't appear to make any difference. // Also set Layout policy to SetMinAndMaxSize as well. Any idea how to fix this? // FIXME //resize(1000,750); summaryPreview.reset(new FITSView(previewWidget, FITS_NORMAL)); previewWidget->setContentsMargins(0, 0, 0, 0); summaryPreview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); summaryPreview->setBaseSize(previewWidget->size()); summaryPreview->createFloatingToolBar(); summaryPreview->setCursorMode(FITSView::dragCursor); QVBoxLayout * vlayout = new QVBoxLayout(); vlayout->addWidget(summaryPreview.get()); previewWidget->setLayout(vlayout); // JM 2019-01-19: Why cloud images depend on summary preview? // connect(summaryPreview.get(), &FITSView::loaded, [&]() // { // // UUID binds the cloud & preview frames by a common key // QString uuid = QUuid::createUuid().toString(); // uuid = uuid.remove(QRegularExpression("[-{}]")); // //ekosLiveClient.get()->media()->sendPreviewImage(summaryPreview.get(), uuid); // ekosLiveClient.get()->cloud()->sendPreviewImage(summaryPreview.get(), uuid); // }); if (Options::ekosLeftIcons()) { toolsWidget->setTabPosition(QTabWidget::West); QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(0); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(0, icon); icon = toolsWidget->tabIcon(1); pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(1, icon); } //Note: This is to prevent a button from being called the default button //and then executing when the user hits the enter key such as when on a Text Box QList qButtons = findChildren(); for (auto &button : qButtons) button->setAutoDefault(false); resize(Options::ekosWindowWidth(), Options::ekosWindowHeight()); } void Manager::changeAlwaysOnTop(Qt::ApplicationState state) { if (isVisible()) { if (state == Qt::ApplicationActive) setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint); else setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); show(); } } Manager::~Manager() { toolsWidget->disconnect(this); //delete previewPixmap; } void Manager::closeEvent(QCloseEvent * event) { // QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); // a->setChecked(false); // 2019-02-14 JM: Close event, for some reason, make all the children disappear // when the widget is shown again. Applying a workaround here event->ignore(); hide(); } void Manager::hideEvent(QHideEvent * /*event*/) { Options::setEkosWindowWidth(width()); Options::setEkosWindowHeight(height()); QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); a->setChecked(false); } void Manager::showEvent(QShowEvent * /*event*/) { QAction * a = KStars::Instance()->actionCollection()->action("show_ekos"); a->setChecked(true); // Just show the profile wizard ONCE per session if (profileWizardLaunched == false && profiles.count() == 1) { profileWizardLaunched = true; wizardProfile(); } } void Manager::resizeEvent(QResizeEvent *) { //previewImage->setPixmap(previewPixmap->scaled(previewImage->width(), previewImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); if (focusStarPixmap.get() != nullptr) focusStarImage->setPixmap(focusStarPixmap->scaled(focusStarImage->width(), focusStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); //if (focusProfilePixmap) //focusProfileImage->setPixmap(focusProfilePixmap->scaled(focusProfileImage->width(), focusProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); if (guideStarPixmap.get() != nullptr) guideStarImage->setPixmap(guideStarPixmap->scaled(guideStarImage->width(), guideStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); //if (guideProfilePixmap) //guideProfileImage->setPixmap(guideProfilePixmap->scaled(guideProfileImage->width(), guideProfileImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } void Manager::loadProfiles() { profiles.clear(); KStarsData::Instance()->userdb()->GetAllProfiles(profiles); profileModel->clear(); for (auto &pi : profiles) { QList info; info << new QStandardItem(pi->id) << new QStandardItem(pi->name) << new QStandardItem(pi->host) << new QStandardItem(pi->port); profileModel->appendRow(info); } profileModel->sort(0); profileCombo->blockSignals(true); profileCombo->setModel(profileModel.get()); profileCombo->setModelColumn(1); profileCombo->blockSignals(false); // Load last used profile from options int index = profileCombo->findText(Options::profile()); // If not found, set it to first item if (index == -1) index = 0; profileCombo->setCurrentIndex(index); } void Manager::loadDrivers() { foreach (DriverInfo * dv, DriverManager::Instance()->getDrivers()) { if (dv->getDriverSource() != HOST_SOURCE) driversList[dv->getLabel()] = dv; } } void Manager::reset() { qCDebug(KSTARS_EKOS) << "Resetting Ekos Manager..."; // Filter Manager filterManager.reset(new Ekos::FilterManager()); nDevices = 0; useGuideHead = false; useST4 = false; removeTabs(); genericDevices.clear(); managedDevices.clear(); captureProcess.reset(); focusProcess.reset(); guideProcess.reset(); domeProcess.reset(); alignProcess.reset(); mountProcess.reset(); weatherProcess.reset(); + observatoryProcess.reset(); dustCapProcess.reset(); Ekos::CommunicationStatus previousStatus = m_ekosStatus; m_ekosStatus = Ekos::Idle; if (previousStatus != m_ekosStatus) emit ekosStatusChanged(m_ekosStatus); previousStatus = m_indiStatus; m_indiStatus = Ekos::Idle; if (previousStatus != m_indiStatus) emit indiStatusChanged(m_indiStatus); connectB->setEnabled(false); disconnectB->setEnabled(false); //controlPanelB->setEnabled(false); processINDIB->setEnabled(true); mountGroup->setEnabled(false); focusGroup->setEnabled(false); captureGroup->setEnabled(false); guideGroup->setEnabled(false); sequenceLabel->setText(i18n("Sequence")); sequenceProgress->setValue(0); captureProgress->setValue(0); overallRemainingTime->setText("--:--:--"); sequenceRemainingTime->setText("--:--:--"); imageRemainingTime->setText("--:--:--"); mountStatus->setText(i18n("Idle")); mountStatus->setStyleSheet(QString()); captureStatus->setText(i18n("Idle")); focusStatus->setText(i18n("Idle")); guideStatus->setText(i18n("Idle")); if (capturePI) capturePI->stopAnimation(); if (mountPI) mountPI->stopAnimation(); if (focusPI) focusPI->stopAnimation(); if (guidePI) guidePI->stopAnimation(); m_isStarted = false; //processINDIB->setText(i18n("Start INDI")); processINDIB->setIcon(QIcon::fromTheme("media-playback-start")); processINDIB->setToolTip(i18n("Start")); } void Manager::processINDI() { if (m_isStarted == false) start(); else stop(); } bool Manager::stop() { cleanDevices(); serialPortAssistant.reset(); serialPortAssistantB->setEnabled(false); profileGroup->setEnabled(true); setWindowTitle(i18n("Ekos")); return true; } bool Manager::start() { // Don't start if it is already started before if (m_ekosStatus == Ekos::Pending || m_ekosStatus == Ekos::Success) { qCDebug(KSTARS_EKOS) << "Ekos Manager start called but current Ekos Status is" << m_ekosStatus << "Ignoring request."; return true; } if (m_LocalMode) qDeleteAll(managedDrivers); managedDrivers.clear(); // If clock was paused, unpaused it and sync time if (KStarsData::Instance()->clock()->isActive() == false) { KStarsData::Instance()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); KStarsData::Instance()->clock()->start(); } reset(); currentProfile = getCurrentProfile(); m_LocalMode = currentProfile->isLocal(); // Load profile location if one exists updateProfileLocation(currentProfile); bool haveCCD = false, haveGuider = false; if (currentProfile->guidertype == Ekos::Guide::GUIDE_PHD2) { Options::setPHD2Host(currentProfile->guiderhost); Options::setPHD2Port(currentProfile->guiderport); } else if (currentProfile->guidertype == Ekos::Guide::GUIDE_LINGUIDER) { Options::setLinGuiderHost(currentProfile->guiderhost); Options::setLinGuiderPort(currentProfile->guiderport); } if (m_LocalMode) { DriverInfo * drv = driversList.value(currentProfile->mount()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->ccd()); if (drv != nullptr) { managedDrivers.append(drv->clone()); haveCCD = true; } Options::setGuiderType(currentProfile->guidertype); drv = driversList.value(currentProfile->guider()); if (drv != nullptr) { haveGuider = true; // If the guider and ccd are the same driver, we have two cases: // #1 Drivers that only support ONE device per driver (such as sbig) // #2 Drivers that supports multiples devices per driver (such as sx) // For #1, we modify guider_di to make a unique label for the other device with postfix "Guide" // For #2, we set guider_di to nullptr and we prompt the user to select which device is primary ccd and which is guider // since this is the only way to find out in real time. if (haveCCD && currentProfile->guider() == currentProfile->ccd()) { if (drv->getAuxInfo().value("mdpd", false).toBool() == true) { drv = nullptr; } else { drv->setUniqueLabel(drv->getLabel() + " Guide"); } } if (drv) managedDrivers.append(drv->clone()); } drv = driversList.value(currentProfile->ao()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->filter()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->focuser()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->dome()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->weather()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->aux1()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->aux2()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->aux3()); if (drv != nullptr) managedDrivers.append(drv->clone()); drv = driversList.value(currentProfile->aux4()); if (drv != nullptr) managedDrivers.append(drv->clone()); // Add remote drivers if we have any if (currentProfile->remotedrivers.isEmpty() == false && currentProfile->remotedrivers.contains("@")) { for (auto remoteDriver : currentProfile->remotedrivers.split(",")) { QString name, label, host("localhost"), port("7624"); QStringList properties = remoteDriver.split(QRegExp("[@:]")); if (properties.length() > 1) { name = properties[0]; host = properties[1]; if (properties.length() > 2) port = properties[2]; } DriverInfo * dv = new DriverInfo(name); dv->setRemoteHost(host); dv->setRemotePort(port); label = name; // Remove extra quotes label.remove("\""); dv->setLabel(label); dv->setUniqueLabel(label); managedDrivers.append(dv); } } if (haveCCD == false && haveGuider == false) { KMessageBox::error(this, i18n("Ekos requires at least one CCD or Guider to operate.")); managedDrivers.clear(); return false; } nDevices = managedDrivers.count(); } else { DriverInfo * remote_indi = new DriverInfo(QString("Ekos Remote Host")); remote_indi->setHostParameters(currentProfile->host, QString::number(currentProfile->port)); remote_indi->setDriverSource(GENERATED_SOURCE); managedDrivers.append(remote_indi); haveCCD = currentProfile->drivers.contains("CCD"); haveGuider = currentProfile->drivers.contains("Guider"); Options::setGuiderType(currentProfile->guidertype); if (haveCCD == false && haveGuider == false) { KMessageBox::error(this, i18n("Ekos requires at least one CCD or Guider to operate.")); delete (remote_indi); nDevices = 0; return false; } nDevices = currentProfile->drivers.count(); } connect(INDIListener::Instance(), &INDIListener::newDevice, this, &Ekos::Manager::processNewDevice); connect(INDIListener::Instance(), &INDIListener::newTelescope, this, &Ekos::Manager::setTelescope); connect(INDIListener::Instance(), &INDIListener::newCCD, this, &Ekos::Manager::setCCD); connect(INDIListener::Instance(), &INDIListener::newFilter, this, &Ekos::Manager::setFilter); connect(INDIListener::Instance(), &INDIListener::newFocuser, this, &Ekos::Manager::setFocuser); connect(INDIListener::Instance(), &INDIListener::newDome, this, &Ekos::Manager::setDome); connect(INDIListener::Instance(), &INDIListener::newWeather, this, &Ekos::Manager::setWeather); connect(INDIListener::Instance(), &INDIListener::newDustCap, this, &Ekos::Manager::setDustCap); connect(INDIListener::Instance(), &INDIListener::newLightBox, this, &Ekos::Manager::setLightBox); connect(INDIListener::Instance(), &INDIListener::newST4, this, &Ekos::Manager::setST4); connect(INDIListener::Instance(), &INDIListener::deviceRemoved, this, &Ekos::Manager::removeDevice, Qt::DirectConnection); #ifdef Q_OS_OSX if (m_LocalMode || currentProfile->host == "localhost") { if (isRunning("PTPCamera")) { if (KMessageBox::Yes == (KMessageBox::questionYesNo(0, i18n("Ekos detected that PTP Camera is running and may prevent a Canon or Nikon camera from connecting to Ekos. Do you want to quit PTP Camera now?"), i18n("PTP Camera"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "ekos_shutdown_PTPCamera"))) { //TODO is there a better way to do this. QProcess p; p.start("killall PTPCamera"); p.waitForFinished(); } } } #endif if (m_LocalMode) { if (isRunning("indiserver")) { if (KMessageBox::Yes == (KMessageBox::questionYesNo(nullptr, i18n("Ekos detected an instance of INDI server running. Do you wish to " "shut down the existing instance before starting a new one?"), i18n("INDI Server"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "ekos_shutdown_existing_indiserver"))) { DriverManager::Instance()->stopAllDevices(); //TODO is there a better way to do this. QProcess p; p.start("pkill indiserver"); p.waitForFinished(); } } appendLogText(i18n("Starting INDI services...")); if (DriverManager::Instance()->startDevices(managedDrivers) == false) { INDIListener::Instance()->disconnect(this); qDeleteAll(managedDrivers); managedDrivers.clear(); m_ekosStatus = Ekos::Error; emit ekosStatusChanged(m_ekosStatus); return false; } connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString, QString)), this, SLOT(processServerTermination(QString, QString))); m_ekosStatus = Ekos::Pending; emit ekosStatusChanged(m_ekosStatus); if (currentProfile->autoConnect) appendLogText(i18n("INDI services started on port %1.", managedDrivers.first()->getPort())); else appendLogText( i18n("INDI services started on port %1. Please connect devices.", managedDrivers.first()->getPort())); QTimer::singleShot(MAX_LOCAL_INDI_TIMEOUT, this, &Ekos::Manager::checkINDITimeout); } else { // If we need to use INDI Web Manager if (currentProfile->INDIWebManagerPort > 0) { appendLogText(i18n("Establishing communication with remote INDI Web Manager...")); m_RemoteManagerStart = false; if (INDI::WebManager::isOnline(currentProfile)) { INDI::WebManager::syncCustomDrivers(currentProfile); currentProfile->isStellarMate = INDI::WebManager::isStellarMate(currentProfile); if (INDI::WebManager::areDriversRunning(currentProfile) == false) { INDI::WebManager::stopProfile(currentProfile); if (INDI::WebManager::startProfile(currentProfile) == false) { appendLogText(i18n("Failed to start profile on remote INDI Web Manager.")); return false; } appendLogText(i18n("Starting profile on remote INDI Web Manager...")); m_RemoteManagerStart = true; } } else appendLogText(i18n("Warning: INDI Web Manager is not online.")); } appendLogText( i18n("Connecting to remote INDI server at %1 on port %2 ...", currentProfile->host, currentProfile->port)); qApp->processEvents(); QApplication::setOverrideCursor(Qt::WaitCursor); if (DriverManager::Instance()->connectRemoteHost(managedDrivers.first()) == false) { appendLogText(i18n("Failed to connect to remote INDI server.")); INDIListener::Instance()->disconnect(this); qDeleteAll(managedDrivers); managedDrivers.clear(); m_ekosStatus = Ekos::Error; emit ekosStatusChanged(m_ekosStatus); QApplication::restoreOverrideCursor(); return false; } connect(DriverManager::Instance(), SIGNAL(serverTerminated(QString, QString)), this, SLOT(processServerTermination(QString, QString))); QApplication::restoreOverrideCursor(); m_ekosStatus = Ekos::Pending; emit ekosStatusChanged(m_ekosStatus); appendLogText( i18n("INDI services started. Connection to remote INDI server is successful. Waiting for devices...")); QTimer::singleShot(MAX_REMOTE_INDI_TIMEOUT, this, &Ekos::Manager::checkINDITimeout); } connectB->setEnabled(false); disconnectB->setEnabled(false); //controlPanelB->setEnabled(false); profileGroup->setEnabled(false); m_isStarted = true; //processINDIB->setText(i18n("Stop INDI")); processINDIB->setIcon(QIcon::fromTheme("media-playback-stop")); processINDIB->setToolTip(i18n("Stop")); setWindowTitle(i18n("Ekos - %1 Profile", currentProfile->name)); return true; } void Manager::checkINDITimeout() { // Don't check anything unless we're still pending if (m_ekosStatus != Ekos::Pending) return; if (nDevices <= 0) { m_ekosStatus = Ekos::Success; emit ekosStatusChanged(m_ekosStatus); return; } if (m_LocalMode) { QStringList remainingDevices; foreach (DriverInfo * drv, managedDrivers) { if (drv->getDevices().count() == 0) remainingDevices << QString("+ %1").arg( drv->getUniqueLabel().isEmpty() == false ? drv->getUniqueLabel() : drv->getName()); } if (remainingDevices.count() == 1) { appendLogText(i18n("Unable to establish:\n%1\nPlease ensure the device is connected and powered on.", remainingDevices.at(0))); KNotification::beep(i18n("Ekos startup error")); } else { appendLogText(i18n("Unable to establish the following devices:\n%1\nPlease ensure each device is connected " "and powered on.", remainingDevices.join("\n"))); KNotification::beep(i18n("Ekos startup error")); } } else { QStringList remainingDevices; for (auto &driver : currentProfile->drivers.values()) { bool driverFound = false; for (auto &device : genericDevices) { if (device->getBaseDevice()->getDriverName() == driver) { driverFound = true; break; } } if (driverFound == false) remainingDevices << QString("+ %1").arg(driver); } if (remainingDevices.count() == 1) { appendLogText(i18n("Unable to establish remote device:\n%1\nPlease ensure remote device name corresponds " "to actual device name.", remainingDevices.at(0))); KNotification::beep(i18n("Ekos startup error")); } else { appendLogText(i18n("Unable to establish remote devices:\n%1\nPlease ensure remote device name corresponds " "to actual device name.", remainingDevices.join("\n"))); KNotification::beep(i18n("Ekos startup error")); } } m_ekosStatus = Ekos::Error; } void Manager::connectDevices() { // Check if already connected int nConnected = 0; Ekos::CommunicationStatus previousStatus = m_indiStatus; for (auto &device : genericDevices) { if (device->isConnected()) nConnected++; } if (genericDevices.count() == nConnected) { m_indiStatus = Ekos::Success; emit indiStatusChanged(m_indiStatus); return; } m_indiStatus = Ekos::Pending; if (previousStatus != m_indiStatus) emit indiStatusChanged(m_indiStatus); for (auto &device : genericDevices) { qCDebug(KSTARS_EKOS) << "Connecting " << device->getDeviceName(); device->Connect(); } connectB->setEnabled(false); disconnectB->setEnabled(true); appendLogText(i18n("Connecting INDI devices...")); } void Manager::disconnectDevices() { for (auto &device : genericDevices) { qCDebug(KSTARS_EKOS) << "Disconnecting " << device->getDeviceName(); device->Disconnect(); } appendLogText(i18n("Disconnecting INDI devices...")); } void Manager::processServerTermination(const QString &host, const QString &port) { if ((m_LocalMode && managedDrivers.first()->getPort() == port) || (currentProfile->host == host && currentProfile->port == port.toInt())) { cleanDevices(false); } } void Manager::cleanDevices(bool stopDrivers) { if (m_ekosStatus == Ekos::Idle) return; INDIListener::Instance()->disconnect(this); DriverManager::Instance()->disconnect(this); if (managedDrivers.isEmpty() == false) { if (m_LocalMode) { if (stopDrivers) DriverManager::Instance()->stopDevices(managedDrivers); } else { if (stopDrivers) DriverManager::Instance()->disconnectRemoteHost(managedDrivers.first()); if (m_RemoteManagerStart && currentProfile->INDIWebManagerPort != -1) { INDI::WebManager::stopProfile(currentProfile); m_RemoteManagerStart = false; } } } reset(); profileGroup->setEnabled(true); appendLogText(i18n("INDI services stopped.")); } void Manager::processNewDevice(ISD::GDInterface * devInterface) { qCInfo(KSTARS_EKOS) << "Ekos received a new device: " << devInterface->getDeviceName(); Ekos::CommunicationStatus previousStatus = m_indiStatus; for(auto &device : genericDevices) { if (!strcmp(device->getDeviceName(), devInterface->getDeviceName())) { qCWarning(KSTARS_EKOS) << "Found duplicate device, ignoring..."; return; } } // Always reset INDI Connection status if we receive a new device m_indiStatus = Ekos::Idle; if (previousStatus != m_indiStatus) emit indiStatusChanged(m_indiStatus); genericDevices.append(devInterface); nDevices--; connect(devInterface, &ISD::GDInterface::Connected, this, &Ekos::Manager::deviceConnected); connect(devInterface, &ISD::GDInterface::Disconnected, this, &Ekos::Manager::deviceDisconnected); connect(devInterface, &ISD::GDInterface::propertyDefined, this, &Ekos::Manager::processNewProperty); connect(devInterface, &ISD::GDInterface::interfaceDefined, this, &Ekos::Manager::syncActiveDevices); if (currentProfile->isStellarMate) { connect(devInterface, &ISD::GDInterface::systemPortDetected, [this, devInterface]() { if (!serialPortAssistant) { serialPortAssistant.reset(new SerialPortAssistant(currentProfile, this)); serialPortAssistantB->setEnabled(true); } uint32_t driverInterface = devInterface->getDriverInterface(); // Ignore CCD interface if (driverInterface & INDI::BaseDevice::CCD_INTERFACE) return; if (driverInterface & INDI::BaseDevice::TELESCOPE_INTERFACE || driverInterface & INDI::BaseDevice::FOCUSER_INTERFACE || driverInterface & INDI::BaseDevice::FILTER_INTERFACE || driverInterface & INDI::BaseDevice::AUX_INTERFACE || driverInterface & INDI::BaseDevice::GPS_INTERFACE) serialPortAssistant->addDevice(devInterface); if (Options::autoLoadSerialAssistant()) serialPortAssistant->show(); }); } if (nDevices <= 0) { m_ekosStatus = Ekos::Success; emit ekosStatusChanged(m_ekosStatus); connectB->setEnabled(true); disconnectB->setEnabled(false); //controlPanelB->setEnabled(true); if (m_LocalMode == false && nDevices == 0) { if (currentProfile->autoConnect) appendLogText(i18n("Remote devices established.")); else appendLogText(i18n("Remote devices established. Please connect devices.")); } } } void Manager::deviceConnected() { connectB->setEnabled(false); disconnectB->setEnabled(true); processINDIB->setEnabled(false); Ekos::CommunicationStatus previousStatus = m_indiStatus; if (Options::verboseLogging()) { ISD::GDInterface * device = qobject_cast(sender()); qCInfo(KSTARS_EKOS) << device->getDeviceName() << "is connected."; } int nConnectedDevices = 0; foreach (ISD::GDInterface * device, genericDevices) { if (device->isConnected()) nConnectedDevices++; } qCDebug(KSTARS_EKOS) << nConnectedDevices << " devices connected out of " << genericDevices.count(); //if (nConnectedDevices >= pi->drivers.count()) if (nConnectedDevices >= genericDevices.count()) { m_indiStatus = Ekos::Success; qCInfo(KSTARS_EKOS) << "All INDI devices are now connected."; } else m_indiStatus = Ekos::Pending; if (previousStatus != m_indiStatus) emit indiStatusChanged(m_indiStatus); ISD::GDInterface * dev = static_cast(sender()); if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE) { if (mountProcess.get() != nullptr) { mountProcess->setEnabled(true); if (alignProcess.get() != nullptr) alignProcess->setEnabled(true); } } else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE) { if (captureProcess.get() != nullptr) captureProcess->setEnabled(true); if (focusProcess.get() != nullptr) focusProcess->setEnabled(true); if (alignProcess.get() != nullptr) { if (mountProcess.get() && mountProcess->isEnabled()) alignProcess->setEnabled(true); else alignProcess->setEnabled(false); } if (guideProcess.get() != nullptr) guideProcess->setEnabled(true); } else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE) { if (focusProcess.get() != nullptr) focusProcess->setEnabled(true); } if (Options::neverLoadConfig()) return; INDIConfig tConfig = Options::loadConfigOnConnection() ? LOAD_LAST_CONFIG : LOAD_DEFAULT_CONFIG; foreach (ISD::GDInterface * device, genericDevices) { if (device == dev) { connect(dev, &ISD::GDInterface::switchUpdated, this, &Ekos::Manager::watchDebugProperty); ISwitchVectorProperty * configProp = device->getBaseDevice()->getSwitch("CONFIG_PROCESS"); if (configProp && configProp->s == IPS_IDLE) device->setConfig(tConfig); break; } } } void Manager::deviceDisconnected() { ISD::GDInterface * dev = static_cast(sender()); Ekos::CommunicationStatus previousStatus = m_indiStatus; if (dev != nullptr) { if (dev->getState("CONNECTION") == IPS_ALERT) m_indiStatus = Ekos::Error; else if (dev->getState("CONNECTION") == IPS_BUSY) m_indiStatus = Ekos::Pending; else m_indiStatus = Ekos::Idle; if (Options::verboseLogging()) qCDebug(KSTARS_EKOS) << dev->getDeviceName() << " is disconnected."; appendLogText(i18n("%1 is disconnected.", dev->getDeviceName())); } else m_indiStatus = Ekos::Idle; if (previousStatus != m_indiStatus) emit indiStatusChanged(m_indiStatus); connectB->setEnabled(true); disconnectB->setEnabled(false); processINDIB->setEnabled(true); if (dev != nullptr && dev->getBaseDevice() && (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) { if (mountProcess.get() != nullptr) mountProcess->setEnabled(false); } // Do not disable modules on device connection loss, let them handle it /* else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::CCD_INTERFACE) { if (captureProcess.get() != nullptr) captureProcess->setEnabled(false); if (focusProcess.get() != nullptr) focusProcess->setEnabled(false); if (alignProcess.get() != nullptr) alignProcess->setEnabled(false); if (guideProcess.get() != nullptr) guideProcess->setEnabled(false); } else if (dev->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::FOCUSER_INTERFACE) { if (focusProcess.get() != nullptr) focusProcess->setEnabled(false); }*/ } void Manager::setTelescope(ISD::GDInterface * scopeDevice) { //mount = scopeDevice; managedDevices[KSTARS_TELESCOPE] = scopeDevice; appendLogText(i18n("%1 is online.", scopeDevice->getDeviceName())); connect(scopeDevice, SIGNAL(numberUpdated(INumberVectorProperty *)), this, SLOT(processNewNumber(INumberVectorProperty *)), Qt::UniqueConnection); initMount(); mountProcess->setTelescope(scopeDevice); double primaryScopeFL = 0, primaryScopeAperture = 0, guideScopeFL = 0, guideScopeAperture = 0; getCurrentProfileTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); // Save telescope info in mount driver mountProcess->setTelescopeInfo(QList() << primaryScopeFL << primaryScopeAperture << guideScopeFL << guideScopeAperture); if (guideProcess.get() != nullptr) { guideProcess->setTelescope(scopeDevice); guideProcess->setTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); } if (alignProcess.get() != nullptr) { alignProcess->setTelescope(scopeDevice); alignProcess->setTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); } if (domeProcess.get() != nullptr) domeProcess->setTelescope(scopeDevice); ekosLiveClient->message()->sendMounts(); ekosLiveClient->message()->sendScopes(); } void Manager::setCCD(ISD::GDInterface * ccdDevice) { // No duplicates for (auto oneCCD : findDevices(KSTARS_CCD)) if (oneCCD == ccdDevice) return; managedDevices.insertMulti(KSTARS_CCD, ccdDevice); initCapture(); captureProcess->setEnabled(true); captureProcess->addCCD(ccdDevice); QString primaryCCD, guiderCCD; // Only look for primary & guider CCDs if we can tell a difference between them // otherwise rely on saved options if (currentProfile->ccd() != currentProfile->guider()) { foreach (ISD::GDInterface * device, findDevices(KSTARS_CCD)) { if (QString(device->getDeviceName()).startsWith(currentProfile->ccd(), Qt::CaseInsensitive)) primaryCCD = QString(device->getDeviceName()); else if (QString(device->getDeviceName()).startsWith(currentProfile->guider(), Qt::CaseInsensitive)) guiderCCD = QString(device->getDeviceName()); } } bool rc = false; if (Options::defaultCaptureCCD().isEmpty() == false) rc = captureProcess->setCamera(Options::defaultCaptureCCD()); if (rc == false && primaryCCD.isEmpty() == false) captureProcess->setCamera(primaryCCD); initFocus(); focusProcess->addCCD(ccdDevice); rc = false; if (Options::defaultFocusCCD().isEmpty() == false) rc = focusProcess->setCamera(Options::defaultFocusCCD()); if (rc == false && primaryCCD.isEmpty() == false) focusProcess->setCamera(primaryCCD); initAlign(); alignProcess->addCCD(ccdDevice); rc = false; if (Options::defaultAlignCCD().isEmpty() == false) rc = alignProcess->setCamera(Options::defaultAlignCCD()); if (rc == false && primaryCCD.isEmpty() == false) alignProcess->setCamera(primaryCCD); initGuide(); guideProcess->addCamera(ccdDevice); rc = false; if (Options::defaultGuideCCD().isEmpty() == false) rc = guideProcess->setCamera(Options::defaultGuideCCD()); if (rc == false && guiderCCD.isEmpty() == false) guideProcess->setCamera(guiderCCD); appendLogText(i18n("%1 is online.", ccdDevice->getDeviceName())); connect(ccdDevice, SIGNAL(numberUpdated(INumberVectorProperty *)), this, SLOT(processNewNumber(INumberVectorProperty *)), Qt::UniqueConnection); if (managedDevices.contains(KSTARS_TELESCOPE)) { alignProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); captureProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); guideProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); } } void Manager::setFilter(ISD::GDInterface * filterDevice) { // No duplicates if (findDevices(KSTARS_FILTER).contains(filterDevice) == false) managedDevices.insertMulti(KSTARS_FILTER, filterDevice); appendLogText(i18n("%1 filter is online.", filterDevice->getDeviceName())); initCapture(); connect(filterDevice, SIGNAL(numberUpdated(INumberVectorProperty *)), this, SLOT(processNewNumber(INumberVectorProperty *)), Qt::UniqueConnection); connect(filterDevice, SIGNAL(textUpdated(ITextVectorProperty *)), this, SLOT(processNewText(ITextVectorProperty *)), Qt::UniqueConnection); captureProcess->addFilter(filterDevice); initFocus(); focusProcess->addFilter(filterDevice); initAlign(); alignProcess->addFilter(filterDevice); if (Options::defaultAlignFW().isEmpty() == false) alignProcess->setFilterWheel(Options::defaultAlignFW()); } void Manager::setFocuser(ISD::GDInterface * focuserDevice) { // No duplicates if (findDevices(KSTARS_FOCUSER).contains(focuserDevice) == false) managedDevices.insertMulti(KSTARS_FOCUSER, focuserDevice); initCapture(); initFocus(); focusProcess->addFocuser(focuserDevice); if (Options::defaultFocusFocuser().isEmpty() == false) focusProcess->setFocuser(Options::defaultFocusFocuser()); appendLogText(i18n("%1 focuser is online.", focuserDevice->getDeviceName())); } void Manager::setDome(ISD::GDInterface * domeDevice) { managedDevices[KSTARS_DOME] = domeDevice; initDome(); domeProcess->setDome(domeDevice); if (captureProcess.get() != nullptr) captureProcess->setDome(domeDevice); if (alignProcess.get() != nullptr) alignProcess->setDome(domeDevice); if (managedDevices.contains(KSTARS_TELESCOPE)) domeProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); appendLogText(i18n("%1 is online.", domeDevice->getDeviceName())); } void Manager::setWeather(ISD::GDInterface * weatherDevice) { managedDevices[KSTARS_WEATHER] = weatherDevice; initWeather(); weatherProcess->setWeather(weatherDevice); appendLogText(i18n("%1 is online.", weatherDevice->getDeviceName())); } void Manager::setDustCap(ISD::GDInterface * dustCapDevice) { // No duplicates if (findDevices(KSTARS_AUXILIARY).contains(dustCapDevice) == false) managedDevices.insertMulti(KSTARS_AUXILIARY, dustCapDevice); initDustCap(); dustCapProcess->setDustCap(dustCapDevice); appendLogText(i18n("%1 is online.", dustCapDevice->getDeviceName())); if (captureProcess.get() != nullptr) captureProcess->setDustCap(dustCapDevice); } void Manager::setLightBox(ISD::GDInterface * lightBoxDevice) { // No duplicates if (findDevices(KSTARS_AUXILIARY).contains(lightBoxDevice) == false) managedDevices.insertMulti(KSTARS_AUXILIARY, lightBoxDevice); if (captureProcess.get() != nullptr) captureProcess->setLightBox(lightBoxDevice); } void Manager::removeDevice(ISD::GDInterface * devInterface) { switch (devInterface->getType()) { case KSTARS_CCD: removeTabs(); break; case KSTARS_TELESCOPE: if (mountProcess.get() != nullptr) { mountProcess.reset(); } break; case KSTARS_FOCUSER: // TODO this should be done for all modules if (focusProcess.get() != nullptr) focusProcess.get()->removeDevice(devInterface); break; default: break; } appendLogText(i18n("%1 is offline.", devInterface->getDeviceName())); // #1 Remove from Generic Devices // Generic devices are ALL the devices we receive from INDI server // Whether Ekos cares about them (i.e. selected equipment) or extra devices we // do not care about foreach (ISD::GDInterface * genericDevice, genericDevices) if (!strcmp(genericDevice->getDeviceName(), devInterface->getDeviceName())) { genericDevices.removeOne(genericDevice); break; } // #2 Remove from Ekos Managed Device // Managed devices are devices selected by the user in the device profile foreach (ISD::GDInterface * device, managedDevices.values()) { if (device == devInterface) { managedDevices.remove(managedDevices.key(device)); if (managedDevices.count() == 0) cleanDevices(); break; } } } void Manager::processNewText(ITextVectorProperty * tvp) { if (!strcmp(tvp->name, "FILTER_NAME")) { ekosLiveClient.get()->message()->sendFilterWheels(); } } void Manager::processNewNumber(INumberVectorProperty * nvp) { if (!strcmp(nvp->name, "TELESCOPE_INFO") && managedDevices.contains(KSTARS_TELESCOPE)) { if (guideProcess.get() != nullptr) { guideProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //guideProcess->syncTelescopeInfo(); } if (alignProcess.get() != nullptr) { alignProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //alignProcess->syncTelescopeInfo(); } if (mountProcess.get() != nullptr) { mountProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //mountProcess->syncTelescopeInfo(); } return; } if (!strcmp(nvp->name, "CCD_INFO") || !strcmp(nvp->name, "GUIDER_INFO") || !strcmp(nvp->name, "CCD_FRAME") || !strcmp(nvp->name, "GUIDER_FRAME")) { if (focusProcess.get() != nullptr) focusProcess->syncCCDInfo(); if (guideProcess.get() != nullptr) guideProcess->syncCCDInfo(); if (alignProcess.get() != nullptr) alignProcess->syncCCDInfo(); return; } /* if (!strcmp(nvp->name, "FILTER_SLOT")) { if (captureProcess.get() != nullptr) captureProcess->checkFilter(); if (focusProcess.get() != nullptr) focusProcess->checkFilter(); if (alignProcess.get() != nullptr) alignProcess->checkFilter(); } */ } void Manager::processNewProperty(INDI::Property * prop) { ISD::GenericDevice * deviceInterface = qobject_cast(sender()); if (!strcmp(prop->getName(), "CONNECTION") && currentProfile->autoConnect) { deviceInterface->Connect(); return; } // Check if we need to turn on DEBUG for logging purposes if (!strcmp(prop->getName(), "DEBUG")) { uint16_t interface = deviceInterface->getBaseDevice()->getDriverInterface(); if ( opsLogs->getINDIDebugInterface() & interface ) { // Check if we need to enable debug logging for the INDI drivers. ISwitchVectorProperty * debugSP = prop->getSwitch(); debugSP->sp[0].s = ISS_ON; debugSP->sp[1].s = ISS_OFF; deviceInterface->getDriverInfo()->getClientManager()->sendNewSwitch(debugSP); } } // Handle debug levels for logging purposes if (!strcmp(prop->getName(), "DEBUG_LEVEL")) { uint16_t interface = deviceInterface->getBaseDevice()->getDriverInterface(); // Check if the logging option for the specific device class is on and if the device interface matches it. if ( opsLogs->getINDIDebugInterface() & interface ) { // Turn on everything ISwitchVectorProperty * debugLevel = prop->getSwitch(); for (int i = 0; i < debugLevel->nsp; i++) debugLevel->sp[i].s = ISS_ON; deviceInterface->getDriverInfo()->getClientManager()->sendNewSwitch(debugLevel); } } if (!strcmp(prop->getName(), "TELESCOPE_INFO") || !strcmp(prop->getName(), "TELESCOPE_SLEW_RATE") || !strcmp(prop->getName(), "TELESCOPE_PARK")) { ekosLiveClient.get()->message()->sendMounts(); ekosLiveClient.get()->message()->sendScopes(); } if (!strcmp(prop->getName(), "CCD_INFO") || !strcmp(prop->getName(), "CCD_TEMPERATURE") || !strcmp(prop->getName(), "CCD_ISO") || !strcmp(prop->getName(), "CCD_GAIN") || !strcmp(prop->getName(), "CCD_CONTROLS")) { ekosLiveClient.get()->message()->sendCameras(); ekosLiveClient.get()->media()->registerCameras(); } if (!strcmp(prop->getName(), "ABS_DOME_POSITION") || !strcmp(prop->getName(), "DOME_ABORT_MOTION") || !strcmp(prop->getName(), "DOME_PARK")) { ekosLiveClient.get()->message()->sendDomes(); } if (!strcmp(prop->getName(), "CAP_PARK") || !strcmp(prop->getName(), "FLAT_LIGHT_CONTROL")) { ekosLiveClient.get()->message()->sendCaps(); } if (!strcmp(prop->getName(), "FILTER_NAME")) ekosLiveClient.get()->message()->sendFilterWheels(); if (!strcmp(prop->getName(), "FILTER_NAME")) filterManager.data()->initFilterProperties(); if (!strcmp(prop->getName(), "CONFIRM_FILTER_SET")) filterManager.data()->initFilterProperties(); if (!strcmp(prop->getName(), "CCD_INFO") || !strcmp(prop->getName(), "GUIDER_INFO")) { if (focusProcess.get() != nullptr) focusProcess->syncCCDInfo(); if (guideProcess.get() != nullptr) guideProcess->syncCCDInfo(); if (alignProcess.get() != nullptr) alignProcess->syncCCDInfo(); return; } if (!strcmp(prop->getName(), "TELESCOPE_INFO") && managedDevices.contains(KSTARS_TELESCOPE)) { if (guideProcess.get() != nullptr) { guideProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //guideProcess->syncTelescopeInfo(); } if (alignProcess.get() != nullptr) { alignProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //alignProcess->syncTelescopeInfo(); } if (mountProcess.get() != nullptr) { mountProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); //mountProcess->syncTelescopeInfo(); } return; } if (!strcmp(prop->getName(), "GUIDER_EXPOSURE")) { foreach (ISD::GDInterface * device, findDevices(KSTARS_CCD)) { if (!strcmp(device->getDeviceName(), prop->getDeviceName())) { initCapture(); initGuide(); useGuideHead = true; captureProcess->addGuideHead(device); guideProcess->addGuideHead(device); bool rc = false; if (Options::defaultGuideCCD().isEmpty() == false) rc = guideProcess->setCamera(Options::defaultGuideCCD()); if (rc == false) guideProcess->setCamera(QString(device->getDeviceName()) + QString(" Guider")); return; } } return; } if (!strcmp(prop->getName(), "CCD_FRAME_TYPE")) { if (captureProcess.get() != nullptr) { foreach (ISD::GDInterface * device, findDevices(KSTARS_CCD)) { if (!strcmp(device->getDeviceName(), prop->getDeviceName())) { captureProcess->syncFrameType(device); return; } } } return; } if (!strcmp(prop->getName(), "TELESCOPE_PARK") && managedDevices.contains(KSTARS_TELESCOPE)) { if (captureProcess.get() != nullptr) captureProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); if (mountProcess.get() != nullptr) mountProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); return; } /* if (!strcmp(prop->getName(), "FILTER_NAME")) { if (captureProcess.get() != nullptr) captureProcess->checkFilter(); if (focusProcess.get() != nullptr) focusProcess->checkFilter(); if (alignProcess.get() != nullptr) alignProcess->checkFilter(); return; } */ if (!strcmp(prop->getName(), "ASTROMETRY_SOLVER")) { foreach (ISD::GDInterface * device, genericDevices) { if (!strcmp(device->getDeviceName(), prop->getDeviceName())) { initAlign(); alignProcess->setAstrometryDevice(device); break; } } } if (!strcmp(prop->getName(), "ABS_ROTATOR_ANGLE")) { managedDevices[KSTARS_ROTATOR] = deviceInterface; if (captureProcess.get() != nullptr) captureProcess->setRotator(deviceInterface); if (alignProcess.get() != nullptr) alignProcess->setRotator(deviceInterface); } if (!strcmp(prop->getName(), "GPS_REFRESH")) { managedDevices.insertMulti(KSTARS_AUXILIARY, deviceInterface); if (mountProcess.get() != nullptr) mountProcess->setGPS(deviceInterface); } if (focusProcess.get() != nullptr && strstr(prop->getName(), "FOCUS_")) { focusProcess->checkFocuser(); } } QList Manager::findDevices(DeviceFamily type) { QList deviceList; QMapIterator i(managedDevices); while (i.hasNext()) { i.next(); if (i.key() == type) deviceList.append(i.value()); } return deviceList; } void Manager::processTabChange() { QWidget * currentWidget = toolsWidget->currentWidget(); //if (focusProcess.get() != nullptr && currentWidget != focusProcess) //focusProcess->resetFrame(); if (alignProcess.get() && alignProcess.get() == currentWidget) { if (alignProcess->isEnabled() == false && captureProcess->isEnabled()) { if (managedDevices[KSTARS_CCD]->isConnected() && managedDevices.contains(KSTARS_TELESCOPE)) { if (alignProcess->isParserOK()) alignProcess->setEnabled(true); //#ifdef Q_OS_WIN else { // If current setting is remote astrometry and profile doesn't contain // remote astrometry, then we switch to online solver. Otherwise, the whole align // module remains disabled and the user cannot change re-enable it since he cannot select online solver ProfileInfo * pi = getCurrentProfile(); if (Options::solverType() == Ekos::Align::SOLVER_REMOTE && pi->aux1() != "Astrometry" && pi->aux2() != "Astrometry" && pi->aux3() != "Astrometry" && pi->aux4() != "Astrometry") { Options::setSolverType(Ekos::Align::SOLVER_ONLINE); alignModule()->setSolverType(Ekos::Align::SOLVER_ONLINE); alignProcess->setEnabled(true); } } //#endif } } alignProcess->checkCCD(); } else if (captureProcess.get() != nullptr && currentWidget == captureProcess.get()) { captureProcess->checkCCD(); } else if (focusProcess.get() != nullptr && currentWidget == focusProcess.get()) { focusProcess->checkCCD(); } else if (guideProcess.get() != nullptr && currentWidget == guideProcess.get()) { guideProcess->checkCCD(); } updateLog(); } void Manager::updateLog() { //if (enableLoggingCheck->isChecked() == false) //return; QWidget * currentWidget = toolsWidget->currentWidget(); if (currentWidget == setupTab) ekosLogOut->setPlainText(m_LogText.join("\n")); else if (currentWidget == alignProcess.get()) ekosLogOut->setPlainText(alignProcess->getLogText()); else if (currentWidget == captureProcess.get()) ekosLogOut->setPlainText(captureProcess->getLogText()); else if (currentWidget == focusProcess.get()) ekosLogOut->setPlainText(focusProcess->getLogText()); else if (currentWidget == guideProcess.get()) ekosLogOut->setPlainText(guideProcess->getLogText()); else if (currentWidget == mountProcess.get()) ekosLogOut->setPlainText(mountProcess->getLogText()); - if (currentWidget == schedulerProcess.get()) + else if (currentWidget == schedulerProcess.get()) ekosLogOut->setPlainText(schedulerProcess->getLogText()); + else if (currentWidget == observatoryProcess.get()) + ekosLogOut->setPlainText(observatoryProcess->getLogText()); #ifdef Q_OS_OSX repaint(); //This is a band-aid for a bug in QT 5.10.0 #endif } void Manager::appendLogText(const QString &text) { m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text)); qCInfo(KSTARS_EKOS) << text; emit newLog(text); updateLog(); } void Manager::clearLog() { QWidget * currentWidget = toolsWidget->currentWidget(); if (currentWidget == setupTab) { m_LogText.clear(); updateLog(); } else if (currentWidget == alignProcess.get()) alignProcess->clearLog(); else if (currentWidget == captureProcess.get()) captureProcess->clearLog(); else if (currentWidget == focusProcess.get()) focusProcess->clearLog(); else if (currentWidget == guideProcess.get()) guideProcess->clearLog(); else if (currentWidget == mountProcess.get()) mountProcess->clearLog(); else if (currentWidget == schedulerProcess.get()) schedulerProcess->clearLog(); } void Manager::initCapture() { if (captureProcess.get() != nullptr) return; captureProcess.reset(new Ekos::Capture()); captureProcess->setEnabled(false); int index = toolsWidget->addTab(captureProcess.get(), QIcon(":/icons/ekos_ccd.png"), ""); toolsWidget->tabBar()->setTabToolTip(index, i18nc("Charge-Coupled Device", "CCD")); if (Options::ekosLeftIcons()) { QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(index); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(index, icon); } connect(captureProcess.get(), &Ekos::Capture::newLog, this, &Ekos::Manager::updateLog); connect(captureProcess.get(), &Ekos::Capture::newStatus, this, &Ekos::Manager::updateCaptureStatus); connect(captureProcess.get(), &Ekos::Capture::newImage, this, &Ekos::Manager::updateCaptureProgress); connect(captureProcess.get(), &Ekos::Capture::newSequenceImage, [&](const QString & filename, const QString & previewFITS) { if (Options::useSummaryPreview() && QFile::exists(filename)) { if (Options::autoImageToFITS()) { if (previewFITS.isEmpty() == false) summaryPreview->loadFITS(previewFITS); } else summaryPreview->loadFITS(filename); } }); connect(captureProcess.get(), &Ekos::Capture::newExposureProgress, this, &Ekos::Manager::updateExposureProgress); connect(captureProcess.get(), &Ekos::Capture::sequenceChanged, ekosLiveClient.get()->message(), &EkosLive::Message::sendCaptureSequence); connect(captureProcess.get(), &Ekos::Capture::settingsUpdated, ekosLiveClient.get()->message(), &EkosLive::Message::sendCaptureSettings); captureGroup->setEnabled(true); sequenceProgress->setEnabled(true); captureProgress->setEnabled(true); imageProgress->setEnabled(true); captureProcess->setFilterManager(filterManager); if (!capturePI) { capturePI = new QProgressIndicator(captureProcess.get()); captureStatusLayout->insertWidget(0, capturePI); } foreach (ISD::GDInterface * device, findDevices(KSTARS_AUXILIARY)) { if (device->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE) captureProcess->setDustCap(device); if (device->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::LIGHTBOX_INTERFACE) captureProcess->setLightBox(device); } if (managedDevices.contains(KSTARS_DOME)) { captureProcess->setDome(managedDevices[KSTARS_DOME]); } if (managedDevices.contains(KSTARS_ROTATOR)) { captureProcess->setRotator(managedDevices[KSTARS_ROTATOR]); } connectModules(); emit newModule("Capture"); } void Manager::initAlign() { if (alignProcess.get() != nullptr) return; alignProcess.reset(new Ekos::Align(currentProfile)); double primaryScopeFL = 0, primaryScopeAperture = 0, guideScopeFL = 0, guideScopeAperture = 0; getCurrentProfileTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); alignProcess->setTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); alignProcess->setEnabled(false); int index = toolsWidget->addTab(alignProcess.get(), QIcon(":/icons/ekos_align.png"), ""); toolsWidget->tabBar()->setTabToolTip(index, i18n("Align")); connect(alignProcess.get(), &Ekos::Align::newLog, this, &Ekos::Manager::updateLog); if (Options::ekosLeftIcons()) { QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(index); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(index, icon); } alignProcess->setFilterManager(filterManager); if (managedDevices.contains(KSTARS_DOME)) { alignProcess->setDome(managedDevices[KSTARS_DOME]); } if (managedDevices.contains(KSTARS_ROTATOR)) { alignProcess->setRotator(managedDevices[KSTARS_ROTATOR]); } connectModules(); emit newModule("Align"); } void Manager::initFocus() { if (focusProcess.get() != nullptr) return; focusProcess.reset(new Ekos::Focus()); int index = toolsWidget->addTab(focusProcess.get(), QIcon(":/icons/ekos_focus.png"), ""); toolsWidget->tabBar()->setTabToolTip(index, i18n("Focus")); // Focus <---> Manager connections connect(focusProcess.get(), &Ekos::Focus::newLog, this, &Ekos::Manager::updateLog); connect(focusProcess.get(), &Ekos::Focus::newStatus, this, &Ekos::Manager::setFocusStatus); connect(focusProcess.get(), &Ekos::Focus::newStarPixmap, this, &Ekos::Manager::updateFocusStarPixmap); connect(focusProcess.get(), &Ekos::Focus::newProfilePixmap, this, &Ekos::Manager::updateFocusProfilePixmap); connect(focusProcess.get(), &Ekos::Focus::newHFR, this, &Ekos::Manager::updateCurrentHFR); // Focus <---> Filter Manager connections focusProcess->setFilterManager(filterManager); connect(filterManager.data(), &Ekos::FilterManager::checkFocus, focusProcess.get(), &Ekos::Focus::checkFocus, Qt::UniqueConnection); connect(focusProcess.get(), &Ekos::Focus::newStatus, filterManager.data(), &Ekos::FilterManager::setFocusStatus, Qt::UniqueConnection); connect(filterManager.data(), &Ekos::FilterManager::newFocusOffset, focusProcess.get(), &Ekos::Focus::adjustFocusOffset, Qt::UniqueConnection); connect(focusProcess.get(), &Ekos::Focus::focusPositionAdjusted, filterManager.data(), &Ekos::FilterManager::setFocusOffsetComplete, Qt::UniqueConnection); connect(focusProcess.get(), &Ekos::Focus::absolutePositionChanged, filterManager.data(), &Ekos::FilterManager::setFocusAbsolutePosition, Qt::UniqueConnection); if (Options::ekosLeftIcons()) { QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(index); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(index, icon); } focusGroup->setEnabled(true); if (!focusPI) { focusPI = new QProgressIndicator(focusProcess.get()); focusStatusLayout->insertWidget(0, focusPI); } connectModules(); emit newModule("Focus"); } void Manager::updateCurrentHFR(double newHFR, int position) { currentHFR->setText(QString("%1").arg(newHFR, 0, 'f', 2) + " px"); QJsonObject cStatus = { {"hfr", newHFR}, {"pos", position} }; ekosLiveClient.get()->message()->updateFocusStatus(cStatus); } void Manager::updateSigmas(double ra, double de) { errRA->setText(QString::number(ra, 'f', 2) + "\""); errDEC->setText(QString::number(de, 'f', 2) + "\""); QJsonObject cStatus = { {"rarms", ra}, {"derms", de} }; ekosLiveClient.get()->message()->updateGuideStatus(cStatus); } void Manager::initMount() { if (mountProcess.get() != nullptr) return; mountProcess.reset(new Ekos::Mount()); int index = toolsWidget->addTab(mountProcess.get(), QIcon(":/icons/ekos_mount.png"), ""); toolsWidget->tabBar()->setTabToolTip(index, i18n("Mount")); connect(mountProcess.get(), &Ekos::Mount::newLog, this, &Ekos::Manager::updateLog); connect(mountProcess.get(), &Ekos::Mount::newCoords, this, &Ekos::Manager::updateMountCoords); connect(mountProcess.get(), &Ekos::Mount::newStatus, this, &Ekos::Manager::updateMountStatus); connect(mountProcess.get(), &Ekos::Mount::newTarget, [&](const QString & target) { mountTarget->setText(target); ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", target}})); }); connect(mountProcess.get(), &Ekos::Mount::slewRateChanged, [&](int slewRate) { QJsonObject status = { { "slewRate", slewRate} }; ekosLiveClient.get()->message()->updateMountStatus(status); } ); foreach (ISD::GDInterface * device, findDevices(KSTARS_AUXILIARY)) { if (device->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE) mountProcess->setGPS(device); } if (Options::ekosLeftIcons()) { QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(index); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(index, icon); } if (!mountPI) { mountPI = new QProgressIndicator(mountProcess.get()); mountStatusLayout->insertWidget(0, mountPI); } mountGroup->setEnabled(true); connectModules(); emit newModule("Mount"); } void Manager::initGuide() { if (guideProcess.get() == nullptr) { guideProcess.reset(new Ekos::Guide()); double primaryScopeFL = 0, primaryScopeAperture = 0, guideScopeFL = 0, guideScopeAperture = 0; getCurrentProfileTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); // Save telescope info in mount driver guideProcess->setTelescopeInfo(primaryScopeFL, primaryScopeAperture, guideScopeFL, guideScopeAperture); } //if ( (haveGuider || ccdCount > 1 || useGuideHead) && useST4 && toolsWidget->indexOf(guideProcess) == -1) if ((findDevices(KSTARS_CCD).isEmpty() == false || useGuideHead) && useST4 && toolsWidget->indexOf(guideProcess.get()) == -1) { //if (mount && mount->isConnected()) if (managedDevices.contains(KSTARS_TELESCOPE) && managedDevices[KSTARS_TELESCOPE]->isConnected()) guideProcess->setTelescope(managedDevices[KSTARS_TELESCOPE]); int index = toolsWidget->addTab(guideProcess.get(), QIcon(":/icons/ekos_guide.png"), ""); toolsWidget->tabBar()->setTabToolTip(index, i18n("Guide")); connect(guideProcess.get(), &Ekos::Guide::newLog, this, &Ekos::Manager::updateLog); guideGroup->setEnabled(true); if (!guidePI) { guidePI = new QProgressIndicator(guideProcess.get()); guideStatusLayout->insertWidget(0, guidePI); } connect(guideProcess.get(), &Ekos::Guide::newStatus, this, &Ekos::Manager::updateGuideStatus); connect(guideProcess.get(), &Ekos::Guide::newStarPixmap, this, &Ekos::Manager::updateGuideStarPixmap); connect(guideProcess.get(), &Ekos::Guide::newProfilePixmap, this, &Ekos::Manager::updateGuideProfilePixmap); connect(guideProcess.get(), &Ekos::Guide::newAxisSigma, this, &Ekos::Manager::updateSigmas); if (Options::ekosLeftIcons()) { QTransform trans; trans.rotate(90); QIcon icon = toolsWidget->tabIcon(index); QPixmap pix = icon.pixmap(QSize(48, 48)); icon = QIcon(pix.transformed(trans)); toolsWidget->setTabIcon(index, icon); } } connectModules(); emit newModule("Guide"); } void Manager::initDome() { if (domeProcess.get() != nullptr) return; domeProcess.reset(new Ekos::Dome()); connect(domeProcess.get(), &Ekos::Dome::newStatus, [&](ISD::Dome::Status newStatus) { QJsonObject status = { { "status", ISD::Dome::getStatusString(newStatus)} }; ekosLiveClient.get()->message()->updateDomeStatus(status); }); connect(domeProcess.get(), &Ekos::Dome::azimuthPositionChanged, [&](double pos) { QJsonObject status = { { "az", pos} }; ekosLiveClient.get()->message()->updateDomeStatus(status); }); + initObservatory(nullptr, domeProcess.get()); emit newModule("Dome"); ekosLiveClient->message()->sendDomes(); } void Manager::initWeather() { if (weatherProcess.get() != nullptr) return; weatherProcess.reset(new Ekos::Weather()); + initObservatory(weatherProcess.get(), nullptr); emit newModule("Weather"); } +void Manager::initObservatory(Weather *weather, Dome *dome) +{ + if (observatoryProcess.get() == nullptr) + { + // Initialize the Observatory Module + observatoryProcess.reset(new Ekos::Observatory()); + int index = toolsWidget->addTab(observatoryProcess.get(), QIcon(":/icons/ekos_observatory.png"), ""); + toolsWidget->tabBar()->setTabToolTip(index, i18n("Observatory")); + connect(observatoryProcess.get(), &Ekos::Observatory::newLog, this, &Ekos::Manager::updateLog); + } + + Observatory *obs = observatoryProcess.get(); + if (weather != nullptr) + obs->getWeatherModel()->initModel(weather); + if (dome != nullptr) + obs->getDomeModel()->initModel(dome); + + emit newModule("Observatory"); + +} + void Manager::initDustCap() { if (dustCapProcess.get() != nullptr) return; dustCapProcess.reset(new Ekos::DustCap()); connect(dustCapProcess.get(), &Ekos::DustCap::newStatus, [&](ISD::DustCap::Status newStatus) { QJsonObject status = { { "status", ISD::DustCap::getStatusString(newStatus)} }; ekosLiveClient.get()->message()->updateCapStatus(status); }); connect(dustCapProcess.get(), &Ekos::DustCap::lightToggled, [&](bool enabled) { QJsonObject status = { { "lightS", enabled} }; ekosLiveClient.get()->message()->updateCapStatus(status); }); connect(dustCapProcess.get(), &Ekos::DustCap::lightIntensityChanged, [&](uint16_t value) { QJsonObject status = { { "lightB", value} }; ekosLiveClient.get()->message()->updateCapStatus(status); }); emit newModule("DustCap"); ekosLiveClient->message()->sendCaps(); } void Manager::setST4(ISD::ST4 * st4Driver) { appendLogText(i18n("Guider port from %1 is ready.", st4Driver->getDeviceName())); useST4 = true; initGuide(); guideProcess->addST4(st4Driver); if (Options::defaultST4Driver().isEmpty() == false) guideProcess->setST4(Options::defaultST4Driver()); } void Manager::removeTabs() { disconnect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange); for (int i = 2; i < toolsWidget->count(); i++) toolsWidget->removeTab(i); alignProcess.reset(); captureProcess.reset(); focusProcess.reset(); guideProcess.reset(); mountProcess.reset(); domeProcess.reset(); weatherProcess.reset(); + observatoryProcess.reset(); dustCapProcess.reset(); managedDevices.clear(); connect(toolsWidget, &QTabWidget::currentChanged, this, &Ekos::Manager::processTabChange, Qt::UniqueConnection); } bool Manager::isRunning(const QString &process) { QProcess ps; #ifdef Q_OS_OSX ps.start("pgrep", QStringList() << process); ps.waitForFinished(); QString output = ps.readAllStandardOutput(); return output.length() > 0; #else ps.start("ps", QStringList() << "-o" << "comm" << "--no-headers" << "-C" << process); ps.waitForFinished(); QString output = ps.readAllStandardOutput(); return output.contains(process); #endif } void Manager::addObjectToScheduler(SkyObject * object) { if (schedulerProcess.get() != nullptr) schedulerProcess->addObject(object); } QString Manager::getCurrentJobName() { return schedulerProcess->getCurrentJobName(); } bool Manager::setProfile(const QString &profileName) { int index = profileCombo->findText(profileName); if (index < 0) return false; profileCombo->setCurrentIndex(index); return true; } void Manager::editNamedProfile(const QJsonObject &profileInfo) { ProfileEditor editor(this); setProfile(profileInfo["name"].toString()); currentProfile = getCurrentProfile(); editor.setPi(currentProfile); editor.setSettings(profileInfo); editor.saveProfile(); } void Manager::addNamedProfile(const QJsonObject &profileInfo) { ProfileEditor editor(this); editor.setSettings(profileInfo); editor.saveProfile(); profiles.clear(); loadProfiles(); profileCombo->setCurrentIndex(profileCombo->count() - 1); currentProfile = getCurrentProfile(); } void Manager::deleteNamedProfile(const QString &name) { currentProfile = getCurrentProfile(); for (auto &pi : profiles) { // Do not delete an actively running profile // Do not delete simulator profile if (pi->name == "Simulators" || pi->name != name || (pi.get() == currentProfile && ekosStatus() != Idle)) continue; KStarsData::Instance()->userdb()->DeleteProfile(pi.get()); profiles.clear(); loadProfiles(); currentProfile = getCurrentProfile(); return; } } QJsonObject Manager::getNamedProfile(const QString &name) { QJsonObject profileInfo; // Get current profile for (auto &pi : profiles) { if (name == pi->name) return pi->toJson(); } return QJsonObject(); } QStringList Manager::getProfiles() { QStringList profiles; for (int i = 0; i < profileCombo->count(); i++) profiles << profileCombo->itemText(i); return profiles; } void Manager::addProfile() { ProfileEditor editor(this); if (editor.exec() == QDialog::Accepted) { profiles.clear(); loadProfiles(); profileCombo->setCurrentIndex(profileCombo->count() - 1); } currentProfile = getCurrentProfile(); } void Manager::editProfile() { ProfileEditor editor(this); currentProfile = getCurrentProfile(); editor.setPi(currentProfile); if (editor.exec() == QDialog::Accepted) { int currentIndex = profileCombo->currentIndex(); profiles.clear(); loadProfiles(); profileCombo->setCurrentIndex(currentIndex); } currentProfile = getCurrentProfile(); } void Manager::deleteProfile() { currentProfile = getCurrentProfile(); if (currentProfile->name == "Simulators") return; if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete the profile?"), i18n("Confirm Delete")) == KMessageBox::No) return; KStarsData::Instance()->userdb()->DeleteProfile(currentProfile); profiles.clear(); loadProfiles(); currentProfile = getCurrentProfile(); } void Manager::wizardProfile() { ProfileWizard wz; if (wz.exec() != QDialog::Accepted) return; ProfileEditor editor(this); editor.setProfileName(wz.profileName); editor.setAuxDrivers(wz.selectedAuxDrivers()); if (wz.useInternalServer == false) editor.setHostPort(wz.host, wz.port); editor.setWebManager(wz.useWebManager); editor.setGuiderType(wz.selectedExternalGuider()); // Disable connection options editor.setConnectionOptionsEnabled(false); if (editor.exec() == QDialog::Accepted) { profiles.clear(); loadProfiles(); profileCombo->setCurrentIndex(profileCombo->count() - 1); } currentProfile = getCurrentProfile(); } ProfileInfo * Manager::getCurrentProfile() { ProfileInfo * currProfile = nullptr; // Get current profile for (auto &pi : profiles) { if (profileCombo->currentText() == pi->name) { currProfile = pi.get(); break; } } return currProfile; } void Manager::updateProfileLocation(ProfileInfo * pi) { if (pi->city.isEmpty() == false) { bool cityFound = KStars::Instance()->setGeoLocation(pi->city, pi->province, pi->country); if (cityFound) appendLogText(i18n("Site location updated to %1.", KStarsData::Instance()->geo()->fullName())); else appendLogText(i18n("Failed to update site location to %1. City not found.", KStarsData::Instance()->geo()->fullName())); } } void Manager::updateMountStatus(ISD::Telescope::Status status) { static ISD::Telescope::Status lastStatus = ISD::Telescope::MOUNT_IDLE; if (status == lastStatus) return; lastStatus = status; mountStatus->setText(dynamic_cast(managedDevices[KSTARS_TELESCOPE])->getStatusString(status)); mountStatus->setStyleSheet(QString()); switch (status) { case ISD::Telescope::MOUNT_PARKING: case ISD::Telescope::MOUNT_SLEWING: case ISD::Telescope::MOUNT_MOVING: mountPI->setColor(QColor(KStarsData::Instance()->colorScheme()->colorNamed("TargetColor"))); if (mountPI->isAnimated() == false) mountPI->startAnimation(); break; case ISD::Telescope::MOUNT_TRACKING: mountPI->setColor(Qt::darkGreen); if (mountPI->isAnimated() == false) mountPI->startAnimation(); break; case ISD::Telescope::MOUNT_PARKED: mountStatus->setStyleSheet("font-weight:bold;background-color:red;border:2px solid black;"); if (mountPI->isAnimated()) mountPI->stopAnimation(); break; default: if (mountPI->isAnimated()) mountPI->stopAnimation(); } QJsonObject cStatus = { {"status", mountStatus->text()} }; ekosLiveClient.get()->message()->updateMountStatus(cStatus); } void Manager::updateMountCoords(const QString &ra, const QString &dec, const QString &az, const QString &alt) { raOUT->setText(ra); decOUT->setText(dec); azOUT->setText(az); altOUT->setText(alt); QJsonObject cStatus = { {"ra", dms::fromString(ra, false).Degrees()}, {"de", dms::fromString(dec, true).Degrees()}, {"az", dms::fromString(az, true).Degrees()}, {"at", dms::fromString(alt, true).Degrees()}, }; ekosLiveClient.get()->message()->updateMountStatus(cStatus); } void Manager::updateCaptureStatus(Ekos::CaptureState status) { captureStatus->setText(Ekos::getCaptureStatusString(status)); captureProgress->setValue(captureProcess->getProgressPercentage()); overallCountDown.setHMS(0, 0, 0); overallCountDown = overallCountDown.addSecs(captureProcess->getOverallRemainingTime()); sequenceCountDown.setHMS(0, 0, 0); sequenceCountDown = sequenceCountDown.addSecs(captureProcess->getActiveJobRemainingTime()); if (status != Ekos::CAPTURE_ABORTED && status != Ekos::CAPTURE_COMPLETE && status != Ekos::CAPTURE_IDLE) { if (status == Ekos::CAPTURE_CAPTURING) capturePI->setColor(Qt::darkGreen); else capturePI->setColor(QColor(KStarsData::Instance()->colorScheme()->colorNamed("TargetColor"))); if (capturePI->isAnimated() == false) { capturePI->startAnimation(); countdownTimer.start(); } } else { if (capturePI->isAnimated()) { capturePI->stopAnimation(); countdownTimer.stop(); if (focusStatus->text() == "Complete") { if (focusPI->isAnimated()) focusPI->stopAnimation(); } imageProgress->setValue(0); sequenceLabel->setText(i18n("Sequence")); imageRemainingTime->setText("--:--:--"); overallRemainingTime->setText("--:--:--"); sequenceRemainingTime->setText("--:--:--"); } } QJsonObject cStatus = { {"status", captureStatus->text()}, {"seqt", sequenceRemainingTime->text()}, {"ovt", overallRemainingTime->text()} }; ekosLiveClient.get()->message()->updateCaptureStatus(cStatus); } void Manager::updateCaptureProgress(Ekos::SequenceJob * job) { // Image is set to nullptr only on initial capture start up int completed = job->getCompleted(); // if (job->getUploadMode() == ISD::CCD::UPLOAD_LOCAL) // completed = job->getCompleted() + 1; // else // completed = job->isPreview() ? job->getCompleted() : job->getCompleted() + 1; if (job->isPreview() == false) { sequenceLabel->setText(QString("Job # %1/%2 %3 (%4/%5)") .arg(captureProcess->getActiveJobID() + 1) .arg(captureProcess->getJobCount()) .arg(job->getFullPrefix()) .arg(completed) .arg(job->getCount())); } else sequenceLabel->setText(i18n("Preview")); sequenceProgress->setRange(0, job->getCount()); sequenceProgress->setValue(completed); QJsonObject status = { {"seqv", completed}, {"seqr", job->getCount()}, {"seql", sequenceLabel->text()} }; ekosLiveClient.get()->message()->updateCaptureStatus(status); if (job->getStatus() == SequenceJob::JOB_BUSY) { QString uuid = QUuid::createUuid().toString(); uuid = uuid.remove(QRegularExpression("[-{}]")); // FITSView *image = job->getActiveChip()->getImageView(FITS_NORMAL); // ekosLiveClient.get()->media()->sendPreviewImage(image, uuid); // ekosLiveClient.get()->cloud()->sendPreviewImage(image, uuid); QString filename = job->property("filename").toString(); ekosLiveClient.get()->media()->sendPreviewImage(filename, uuid); if (job->isPreview() == false) ekosLiveClient.get()->cloud()->sendPreviewImage(filename, uuid); } } void Manager::updateExposureProgress(Ekos::SequenceJob * job) { imageCountDown.setHMS(0, 0, 0); imageCountDown = imageCountDown.addSecs(job->getExposeLeft()); if (imageCountDown.hour() == 23) imageCountDown.setHMS(0, 0, 0); imageProgress->setRange(0, job->getExposure()); imageProgress->setValue(job->getExposeLeft()); imageRemainingTime->setText(imageCountDown.toString("hh:mm:ss")); QJsonObject status { {"expv", job->getExposeLeft()}, {"expr", job->getExposure()} }; ekosLiveClient.get()->message()->updateCaptureStatus(status); } void Manager::updateCaptureCountDown() { overallCountDown = overallCountDown.addSecs(-1); if (overallCountDown.hour() == 23) overallCountDown.setHMS(0, 0, 0); sequenceCountDown = sequenceCountDown.addSecs(-1); if (sequenceCountDown.hour() == 23) sequenceCountDown.setHMS(0, 0, 0); overallRemainingTime->setText(overallCountDown.toString("hh:mm:ss")); sequenceRemainingTime->setText(sequenceCountDown.toString("hh:mm:ss")); QJsonObject status = { {"seqt", sequenceRemainingTime->text()}, {"ovt", overallRemainingTime->text()} }; ekosLiveClient.get()->message()->updateCaptureStatus(status); } void Manager::updateFocusStarPixmap(QPixmap &starPixmap) { if (starPixmap.isNull()) return; focusStarPixmap.reset(new QPixmap(starPixmap)); focusStarImage->setPixmap(focusStarPixmap->scaled(focusStarImage->width(), focusStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } void Manager::updateFocusProfilePixmap(QPixmap &profilePixmap) { if (profilePixmap.isNull()) return; focusProfileImage->setPixmap(profilePixmap); } void Manager::setFocusStatus(Ekos::FocusState status) { focusStatus->setText(Ekos::getFocusStatusString(status)); if (status >= Ekos::FOCUS_PROGRESS) { focusPI->setColor(QColor(KStarsData::Instance()->colorScheme()->colorNamed("TargetColor"))); if (focusPI->isAnimated() == false) focusPI->startAnimation(); } else if (status == Ekos::FOCUS_COMPLETE && Options::enforceAutofocus() && captureProcess->getActiveJobID() != -1) { focusPI->setColor(Qt::darkGreen); if (focusPI->isAnimated() == false) focusPI->startAnimation(); } else { if (focusPI->isAnimated()) focusPI->stopAnimation(); } QJsonObject cStatus = { {"status", focusStatus->text()} }; ekosLiveClient.get()->message()->updateFocusStatus(cStatus); } void Manager::updateGuideStatus(Ekos::GuideState status) { guideStatus->setText(Ekos::getGuideStatusString(status)); switch (status) { case Ekos::GUIDE_IDLE: case Ekos::GUIDE_CALIBRATION_ERROR: case Ekos::GUIDE_ABORTED: case Ekos::GUIDE_SUSPENDED: case Ekos::GUIDE_DITHERING_ERROR: case Ekos::GUIDE_CALIBRATION_SUCESS: if (guidePI->isAnimated()) guidePI->stopAnimation(); break; case Ekos::GUIDE_CALIBRATING: guidePI->setColor(QColor(KStarsData::Instance()->colorScheme()->colorNamed("TargetColor"))); if (guidePI->isAnimated() == false) guidePI->startAnimation(); break; case Ekos::GUIDE_GUIDING: guidePI->setColor(Qt::darkGreen); if (guidePI->isAnimated() == false) guidePI->startAnimation(); break; case Ekos::GUIDE_DITHERING: guidePI->setColor(QColor(KStarsData::Instance()->colorScheme()->colorNamed("TargetColor"))); if (guidePI->isAnimated() == false) guidePI->startAnimation(); break; case Ekos::GUIDE_DITHERING_SUCCESS: guidePI->setColor(Qt::darkGreen); if (guidePI->isAnimated() == false) guidePI->startAnimation(); break; default: if (guidePI->isAnimated()) guidePI->stopAnimation(); break; } QJsonObject cStatus = { {"status", guideStatus->text()} }; ekosLiveClient.get()->message()->updateGuideStatus(cStatus); } void Manager::updateGuideStarPixmap(QPixmap &starPix) { if (starPix.isNull()) return; guideStarPixmap.reset(new QPixmap(starPix)); guideStarImage->setPixmap(guideStarPixmap->scaled(guideStarImage->width(), guideStarImage->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } void Manager::updateGuideProfilePixmap(QPixmap &profilePix) { if (profilePix.isNull()) return; guideProfileImage->setPixmap(profilePix); } void Manager::setTarget(SkyObject * o) { mountTarget->setText(o->name()); ekosLiveClient.get()->message()->updateMountStatus(QJsonObject({{"target", o->name()}})); } void Manager::showEkosOptions() { QWidget * currentWidget = toolsWidget->currentWidget(); if (alignProcess.get() && alignProcess.get() == currentWidget) { KConfigDialog * alignSettings = KConfigDialog::exists("alignsettings"); if (alignSettings) { alignSettings->setEnabled(true); alignSettings->show(); } return; } if (guideProcess.get() && guideProcess.get() == currentWidget) { KConfigDialog::showDialog("guidesettings"); return; } if (ekosOptionsWidget == nullptr) { optionsB->click(); } else if (KConfigDialog::showDialog("settings")) { KConfigDialog * cDialog = KConfigDialog::exists("settings"); cDialog->setCurrentPage(ekosOptionsWidget); } } void Manager::getCurrentProfileTelescopeInfo(double &primaryFocalLength, double &primaryAperture, double &guideFocalLength, double &guideAperture) { ProfileInfo * pi = getCurrentProfile(); if (pi) { int primaryScopeID = 0, guideScopeID = 0; primaryScopeID = pi->primaryscope; guideScopeID = pi->guidescope; if (primaryScopeID > 0 || guideScopeID > 0) { // Get all OAL equipment filter list QList m_scopeList; KStarsData::Instance()->userdb()->GetAllScopes(m_scopeList); foreach(OAL::Scope * oneScope, m_scopeList) { if (oneScope->id().toInt() == primaryScopeID) { primaryFocalLength = oneScope->focalLength(); primaryAperture = oneScope->aperture(); } if (oneScope->id().toInt() == guideScopeID) { guideFocalLength = oneScope->focalLength(); guideAperture = oneScope->aperture(); } } } } } void Manager::updateDebugInterfaces() { KSUtils::Logging::SyncFilterRules(); for (ISD::GDInterface * device : genericDevices) { INDI::Property * debugProp = device->getProperty("DEBUG"); ISwitchVectorProperty * debugSP = nullptr; if (debugProp) debugSP = debugProp->getSwitch(); else continue; // Check if the debug interface matches the driver device class if ( ( opsLogs->getINDIDebugInterface() & device->getBaseDevice()->getDriverInterface() ) && debugSP->sp[0].s != ISS_ON) { debugSP->sp[0].s = ISS_ON; debugSP->sp[1].s = ISS_OFF; device->getDriverInfo()->getClientManager()->sendNewSwitch(debugSP); appendLogText(i18n("Enabling debug logging for %1...", device->getDeviceName())); } else if ( !( opsLogs->getINDIDebugInterface() & device->getBaseDevice()->getDriverInterface() ) && debugSP->sp[0].s != ISS_OFF) { debugSP->sp[0].s = ISS_OFF; debugSP->sp[1].s = ISS_ON; device->getDriverInfo()->getClientManager()->sendNewSwitch(debugSP); appendLogText(i18n("Disabling debug logging for %1...", device->getDeviceName())); } if (opsLogs->isINDISettingsChanged()) device->setConfig(SAVE_CONFIG); } } void Manager::watchDebugProperty(ISwitchVectorProperty * svp) { if (!strcmp(svp->name, "DEBUG")) { ISD::GenericDevice * deviceInterface = qobject_cast(sender()); // We don't process pure general interfaces if (deviceInterface->getBaseDevice()->getDriverInterface() == INDI::BaseDevice::GENERAL_INTERFACE) return; // If debug was turned off, but our logging policy requires it then turn it back on. // We turn on debug logging if AT LEAST one driver interface is selected by the logging settings if (svp->s == IPS_OK && svp->sp[0].s == ISS_OFF && (opsLogs->getINDIDebugInterface() & deviceInterface->getBaseDevice()->getDriverInterface())) { svp->sp[0].s = ISS_ON; svp->sp[1].s = ISS_OFF; deviceInterface->getDriverInfo()->getClientManager()->sendNewSwitch(svp); appendLogText(i18n("Re-enabling debug logging for %1...", deviceInterface->getDeviceName())); } // To turn off debug logging, NONE of the driver interfaces should be enabled in logging settings. // For example, if we have CCD+FilterWheel device and CCD + Filter Wheel logging was turned on in // the log settings, then if the user turns off only CCD logging, the debug logging is NOT // turned off until he turns off Filter Wheel logging as well. else if (svp->s == IPS_OK && svp->sp[0].s == ISS_ON && !(opsLogs->getINDIDebugInterface() & deviceInterface->getBaseDevice()->getDriverInterface())) { svp->sp[0].s = ISS_OFF; svp->sp[1].s = ISS_ON; deviceInterface->getDriverInfo()->getClientManager()->sendNewSwitch(svp); appendLogText(i18n("Re-disabling debug logging for %1...", deviceInterface->getDeviceName())); } } } void Manager::announceEvent(const QString &message, KSNotification::EventType event) { ekosLiveClient.get()->message()->sendEvent(message, event); } void Manager::connectModules() { // Guide <---> Capture connections if (captureProcess.get() && guideProcess.get()) { captureProcess.get()->disconnect(guideProcess.get()); guideProcess.get()->disconnect(captureProcess.get()); // Guide Limits connect(guideProcess.get(), &Ekos::Guide::newStatus, captureProcess.get(), &Ekos::Capture::setGuideStatus, Qt::UniqueConnection); connect(guideProcess.get(), &Ekos::Guide::newAxisDelta, captureProcess.get(), &Ekos::Capture::setGuideDeviation); // Dithering connect(captureProcess.get(), &Ekos::Capture::newStatus, guideProcess.get(), &Ekos::Guide::setCaptureStatus, Qt::UniqueConnection); // Guide Head connect(captureProcess.get(), &Ekos::Capture::suspendGuiding, guideProcess.get(), &Ekos::Guide::suspend, Qt::UniqueConnection); connect(captureProcess.get(), &Ekos::Capture::resumeGuiding, guideProcess.get(), &Ekos::Guide::resume, Qt::UniqueConnection); connect(guideProcess.get(), &Ekos::Guide::guideChipUpdated, captureProcess.get(), &Ekos::Capture::setGuideChip, Qt::UniqueConnection); // Meridian Flip connect(captureProcess.get(), &Ekos::Capture::meridianFlipStarted, guideProcess.get(), &Ekos::Guide::abort, Qt::UniqueConnection); connect(captureProcess.get(), &Ekos::Capture::meridianFlipCompleted, guideProcess.get(), [&]() { if (Options::resetGuideCalibration()) guideProcess->clearCalibration(); guideProcess->guide(); }); } // Guide <---> Mount connections if (guideProcess.get() && mountProcess.get()) { // Parking connect(mountProcess.get(), &Ekos::Mount::newStatus, guideProcess.get(), &Ekos::Guide::setMountStatus, Qt::UniqueConnection); } // Focus <---> Guide connections if (guideProcess.get() && focusProcess.get()) { // Suspend connect(focusProcess.get(), &Ekos::Focus::suspendGuiding, guideProcess.get(), &Ekos::Guide::suspend, Qt::UniqueConnection); connect(focusProcess.get(), &Ekos::Focus::resumeGuiding, guideProcess.get(), &Ekos::Guide::resume, Qt::UniqueConnection); } // Capture <---> Focus connections if (captureProcess.get() && focusProcess.get()) { // Check focus HFR value connect(captureProcess.get(), &Ekos::Capture::checkFocus, focusProcess.get(), &Ekos::Focus::checkFocus, Qt::UniqueConnection); // Reset Focus connect(captureProcess.get(), &Ekos::Capture::resetFocus, focusProcess.get(), &Ekos::Focus::resetFrame, Qt::UniqueConnection); // New Focus Status connect(focusProcess.get(), &Ekos::Focus::newStatus, captureProcess.get(), &Ekos::Capture::setFocusStatus, Qt::UniqueConnection); // New Focus HFR connect(focusProcess.get(), &Ekos::Focus::newHFR, captureProcess.get(), &Ekos::Capture::setHFR, Qt::UniqueConnection); } // Capture <---> Align connections if (captureProcess.get() && alignProcess.get()) { // Alignment flag connect(alignProcess.get(), &Ekos::Align::newStatus, captureProcess.get(), &Ekos::Capture::setAlignStatus, Qt::UniqueConnection); // Solver data connect(alignProcess.get(), &Ekos::Align::newSolverResults, captureProcess.get(), &Ekos::Capture::setAlignResults, Qt::UniqueConnection); // Capture Status connect(captureProcess.get(), &Ekos::Capture::newStatus, alignProcess.get(), &Ekos::Align::setCaptureStatus, Qt::UniqueConnection); } // Capture <---> Mount connections if (captureProcess.get() && mountProcess.get()) { // Meridian Flip Setup Values connect(captureProcess.get(), &Ekos::Capture::newMeridianFlipSetup, mountProcess.get(), &Ekos::Mount::setMeridianFlipValues, Qt::UniqueConnection); connect(mountProcess.get(), &Ekos::Mount::newMeridianFlipSetup, captureProcess.get(), &Ekos::Capture::setMeridianFlipValues, Qt::UniqueConnection); // Meridian Flip states connect(captureProcess.get(), &Ekos::Capture::meridianFlipStarted, mountProcess.get(), &Ekos::Mount::disableAltLimits, Qt::UniqueConnection); connect(captureProcess.get(), &Ekos::Capture::meridianFlipCompleted, mountProcess.get(), &Ekos::Mount::enableAltLimits, Qt::UniqueConnection); connect(captureProcess.get(), &Ekos::Capture::newMeridianFlipStatus, mountProcess.get(), &Ekos::Mount::meridianFlipStatusChanged, Qt::UniqueConnection); connect(mountProcess.get(), &Ekos::Mount::newMeridianFlipStatus, captureProcess.get(), &Ekos::Capture::meridianFlipStatusChanged, Qt::UniqueConnection); // Mount Status connect(mountProcess.get(), &Ekos::Mount::newStatus, captureProcess.get(), &Ekos::Capture::setMountStatus, Qt::UniqueConnection); } // Capture <---> EkosLive connections if (captureProcess.get() && ekosLiveClient.get()) { captureProcess.get()->disconnect(ekosLiveClient.get()->message()); connect(captureProcess.get(), &Ekos::Capture::dslrInfoRequested, ekosLiveClient.get()->message(), &EkosLive::Message::requestDSLRInfo); } // Focus <---> Align connections if (focusProcess.get() && alignProcess.get()) { connect(focusProcess.get(), &Ekos::Focus::newStatus, alignProcess.get(), &Ekos::Align::setFocusStatus, Qt::UniqueConnection); } // Focus <---> Mount connections if (focusProcess.get() && mountProcess.get()) { connect(mountProcess.get(), &Ekos::Mount::newStatus, focusProcess.get(), &Ekos::Focus::setMountStatus, Qt::UniqueConnection); } // Mount <---> Align connections if (mountProcess.get() && alignProcess.get()) { connect(mountProcess.get(), &Ekos::Mount::newStatus, alignProcess.get(), &Ekos::Align::setMountStatus, Qt::UniqueConnection); } // Mount <---> Guide connections if (mountProcess.get() && guideProcess.get()) { connect(mountProcess.get(), &Ekos::Mount::pierSideChanged, guideProcess.get(), &Ekos::Guide::setPierSide, Qt::UniqueConnection); } // Focus <---> Align connections if (focusProcess.get() && alignProcess.get()) { connect(focusProcess.get(), &Ekos::Focus::newStatus, alignProcess.get(), &Ekos::Align::setFocusStatus, Qt::UniqueConnection); } // Align <--> EkosLive connections if (alignProcess.get() && ekosLiveClient.get()) { alignProcess.get()->disconnect(ekosLiveClient.get()->message()); alignProcess.get()->disconnect(ekosLiveClient.get()->media()); connect(alignProcess.get(), &Ekos::Align::newStatus, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignStatus); connect(alignProcess.get(), &Ekos::Align::newSolution, ekosLiveClient.get()->message(), &EkosLive::Message::setAlignSolution); connect(alignProcess.get(), &Ekos::Align::newPAHStage, ekosLiveClient.get()->message(), &EkosLive::Message::setPAHStage); connect(alignProcess.get(), &Ekos::Align::newPAHMessage, ekosLiveClient.get()->message(), &EkosLive::Message::setPAHMessage); connect(alignProcess.get(), &Ekos::Align::PAHEnabled, ekosLiveClient.get()->message(), &EkosLive::Message::setPAHEnabled); connect(alignProcess.get(), &Ekos::Align::newImage, [&](FITSView * view) { ekosLiveClient.get()->media()->sendPreviewImage(view, QString()); }); connect(alignProcess.get(), &Ekos::Align::newFrame, ekosLiveClient.get()->media(), &EkosLive::Media::sendUpdatedFrame); connect(alignProcess.get(), &Ekos::Align::polarResultUpdated, ekosLiveClient.get()->message(), &EkosLive::Message::setPolarResults); connect(alignProcess.get(), &Ekos::Align::settingsUpdated, ekosLiveClient.get()->message(), &EkosLive::Message::sendAlignSettings); connect(alignProcess.get(), &Ekos::Align::newCorrectionVector, ekosLiveClient.get()->media(), &EkosLive::Media::setCorrectionVector); } } void Manager::setEkosLiveConnected(bool enabled) { ekosLiveClient.get()->setConnected(enabled); } void Manager::setEkosLiveConfig(bool onlineService, bool rememberCredentials, bool autoConnect) { ekosLiveClient.get()->setConfig(onlineService, rememberCredentials, autoConnect); } void Manager::setEkosLiveUser(const QString &username, const QString &password) { ekosLiveClient.get()->setUser(username, password); } bool Manager::ekosLiveStatus() { return ekosLiveClient.get()->isConnected(); } void Manager::syncActiveDevices() { for (auto oneDevice : genericDevices) { uint32_t devInterface = oneDevice->getDriverInterface(); if (devInterface & (INDI::BaseDevice::TELESCOPE_INTERFACE | INDI::BaseDevice::DOME_INTERFACE)) { for (auto otherDevice : genericDevices) { if (otherDevice == oneDevice) continue; ITextVectorProperty *tvp = otherDevice->getBaseDevice()->getText("ACTIVE_DEVICES"); if (tvp) { IText *snoopProperty = nullptr; if (devInterface & INDI::BaseDevice::TELESCOPE_INTERFACE) snoopProperty = IUFindText(tvp, "ACTIVE_TELESCOPE"); else snoopProperty = IUFindText(tvp, "ACTIVE_DOME"); if (snoopProperty && strcmp(snoopProperty->text, oneDevice->getDeviceName())) { IUSaveText(snoopProperty, oneDevice->getDeviceName()); otherDevice->getDriverInfo()->getClientManager()->sendNewText(tvp); } } } } } } } diff --git a/kstars/ekos/manager.h b/kstars/ekos/manager.h index 1662f6872..b26d877e9 100644 --- a/kstars/ekos/manager.h +++ b/kstars/ekos/manager.h @@ -1,503 +1,506 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application 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. */ #pragma once #ifdef USE_QT5_INDI #include #else #include #endif #include "ui_manager.h" #include "ekos.h" #include "align/align.h" #include "auxiliary/dome.h" #include "auxiliary/weather.h" #include "auxiliary/dustcap.h" #include "capture/capture.h" #include "focus/focus.h" #include "guide/guide.h" #include "indi/indistd.h" #include "mount/mount.h" #include "scheduler/scheduler.h" +#include "observatory/observatory.h" #include "auxiliary/filtermanager.h" #include "auxiliary/serialportassistant.h" #include "ksnotification.h" // Can't use forward declaration with QPointer. QTBUG-29588 #include "auxiliary/opslogs.h" #include #include #include #include //! Generic record interfaces and implementations. namespace EkosLive { class Client; class Message; class Media; } class QProgressIndicator; class DriverInfo; class ProfileInfo; class KPageWidgetItem; /** * @class Manager * @short Primary class to handle all Ekos modules. * The Ekos Manager class manages startup and shutdown of INDI devices and registeration of devices within Ekos Modules. Ekos module consist of \ref Ekos::Mount, \ref Ekos::Capture, \ref Ekos::Focus, \ref Ekos::Guide, and \ref Ekos::Align modules. * \defgroup EkosDBusInterface "Ekos DBus Interface" provides high level functions to control devices and Ekos modules for a total robotic operation: *
    *
  • \ref CaptureDBusInterface "Capture Module DBus Interface"
  • *
  • \ref FocusDBusInterface "Focus Module DBus Interface"
  • *
  • \ref MountDBusInterface "Mount Module DBus Interface"
  • *
  • \ref GuideDBusInterface "Guide Module DBus Interface"
  • *
  • \ref AlignDBusInterface "Align Module DBus Interface"
  • *
  • \ref WeatherDBusInterface "Weather DBus Interface"
  • *
  • \ref DustCapDBusInterface "Dust Cap DBus Interface"
  • *
* For low level access to INDI devices, the \ref INDIDBusInterface "INDI Dbus Interface" provides complete access to INDI devices and properties. * Ekos Manager provides a summary of operations progress in the Summary section of the Setup tab. * * @author Jasem Mutlaq * @version 1.7 */ namespace Ekos { class Manager : public QDialog, public Ui::Manager { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos") Q_SCRIPTABLE Q_PROPERTY(CommunicationStatus indiStatus READ indiStatus NOTIFY indiStatusChanged) Q_SCRIPTABLE Q_PROPERTY(CommunicationStatus ekosStatus READ ekosStatus NOTIFY ekosStatusChanged) Q_SCRIPTABLE Q_PROPERTY(bool ekosLiveStatus READ ekosLiveStatus NOTIFY ekosLiveStatusChanged) Q_SCRIPTABLE Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) public: explicit Manager(QWidget *parent); ~Manager() override; void appendLogText(const QString &); //void refreshRemoteDrivers(); void setOptionsWidget(KPageWidgetItem *ops) { ekosOptionsWidget = ops; } void addObjectToScheduler(SkyObject *object); Guide *guideModule() { return guideProcess.get(); } Align *alignModule() { return alignProcess.get(); } Mount *mountModule() { return mountProcess.get(); } Focus *focusModule() { return focusProcess.get(); } Dome *domeModule() { return domeProcess.get(); } DustCap *capModule() { return dustCapProcess.get(); } Capture *captureModule() { return captureProcess.get(); } FITSView *getSummaryPreview() { return summaryPreview.get(); } QString getCurrentJobName(); void announceEvent(const QString &message, KSNotification::EventType event); /** * @brief addProfile Add a new profile to the database. * @param profileInfo Collection of profile parameters to include the following: * 1. name: Profile name * 2. auto_connect: True of False for Autoconnect? * 3. Mode: "local" or "remote" * 4. remote_host: Optional. remote host (default localhost) * 5. remote_port: Optional. remote port (default 7624) * 6. guiding: 0 for "Internal", 1 for "PHD2", or 2 for "LinGuider" * 7. remote_guiding_host: Optional. remote host for guider application (default localhost) * 8. remote_guide_port: Optional. remote port for guider application. * 9. use_web_manager: True or False? * 10. web_manager_port. Optional. INDI Web Manager port (default 8624) * 12. primary_scope: ID of primary scope to use. This is the ID from OAL::Scope list in the database. * 13. guide_scope: ID of guide scope to use. This is the ID from OAL::Scope list in the database. * 14. mount: Mount driver label (default --). * 15. ccd: CCD driver label (default --). * 16. guider: Guider driver label (default --). * 17. focuser: Focuser driver label (default --). * 18. filter: Filter Wheel driver label (default --). * 19. ao: Adaptive Optics driver label (default --). * 20. dome: Dome driver label (default --). * 21. Weather: Weather station driver label (default --). * 22. aux1: aux1 driver label (default --). * 23. aux2: aux2 driver label (default --). * 24. aux3: aux3 driver label (default --). * 25. aux4: aux4 driver label (default --). */ void addNamedProfile(const QJsonObject &profileInfo); /** Same as above, except it edits an existing named profile */ void editNamedProfile(const QJsonObject &profileInfo); /** * @brief deleteProfile Delete existing equipment profile * @param name Name of profile * @warning Ekos must be stopped for this to work. It will fail if Ekos is online. */ void deleteNamedProfile(const QString &name); /** * @brief getProfile Get a single profile information. * @param name Profile name * @return A JSon object with the detail profile info as described in addProfile function. */ QJsonObject getNamedProfile(const QString &name); /** * DBus commands to manage equipment profiles. */ /*@{*/ /** * DBUS interface function. * set Current device profile. * @param profileName Profile name * @return True if profile is set, false if not found. */ Q_SCRIPTABLE bool setProfile(const QString &profileName); /** * DBUS interface function * @brief getProfiles Return a list of all device profiles * @return List of device profiles */ Q_SCRIPTABLE QStringList getProfiles(); /** @}*/ /** * Manager interface provides advanced scripting capabilities to establish and shutdown Ekos services. */ /*@{*/ /** * DBUS interface function. * @return INDI connection status (0 Idle, 1 Pending, 2 Connected, 3 Error) * @deprecated */ Q_SCRIPTABLE unsigned int getINDIConnectionStatus() { return m_indiStatus; } Q_SCRIPTABLE CommunicationStatus indiStatus() { return m_indiStatus; } /** * DBUS interface function. * @return Ekos starting status (0 Idle, 1 Pending, 2 Started, 3 Error) * @deprecated */ Q_SCRIPTABLE unsigned int getEkosStartingStatus() { return m_ekosStatus; } Q_SCRIPTABLE CommunicationStatus ekosStatus() { return m_ekosStatus; } /** * DBUS interface function. * If connection mode is local, the function first establishes an INDI server with all the specified drivers in Ekos options or as set by the user. For remote connection, * it establishes connection to the remote INDI server. * @return Returns true if server started successful (local mode) or connection to remote server is successful (remote mode). */ Q_SCRIPTABLE bool start(); /** * DBUS interface function. * If connection mode is local, the function terminates the local INDI server and drivers. For remote, it disconnects from the remote INDI server. */ Q_SCRIPTABLE bool stop(); Q_SCRIPTABLE QStringList logText() { return m_LogText; } Q_SCRIPTABLE bool ekosLiveStatus(); /** * DBUS interface function. * @param enabled Connect to EkosLive if true, otherwise disconnect. */ Q_SCRIPTABLE void setEkosLiveConnected(bool enabled); /** * @brief setEkosLiveConfig Set EkosLive settings * @param onlineService If true, connect to EkosLive Online Service. Otherwise, EkosLive offline service. * @param rememberCredentials Remember username and password for next session. * @param autoConnect If true, it will automatically connect to EkosLive service. */ Q_SCRIPTABLE void setEkosLiveConfig(bool onlineService, bool rememberCredentials, bool autoConnect); /** * @brief setEkosLiveUser Save EkosLive username and password * @param username User name * @param password Password */ Q_SCRIPTABLE void setEkosLiveUser(const QString &username, const QString &password); signals: // Have to use full Ekos::CommunicationStatus for DBus signal to work void ekosStatusChanged(Ekos::CommunicationStatus status); void indiStatusChanged(Ekos::CommunicationStatus status); void ekosLiveStatusChanged(bool status); void newLog(const QString &text); void newModule(const QString &name); protected: void closeEvent(QCloseEvent *event) override; void hideEvent(QHideEvent *) override; void showEvent(QShowEvent *) override; void resizeEvent(QResizeEvent *) override; public slots: /** * DBUS interface function. * Connects all the INDI devices started by Ekos. */ Q_SCRIPTABLE Q_NOREPLY void connectDevices(); /** * DBUS interface function. * Disconnects all the INDI devices started by Ekos. */ Q_SCRIPTABLE Q_NOREPLY void disconnectDevices(); /** @}*/ void processINDI(); void cleanDevices(bool stopDrivers = true); void processNewDevice(ISD::GDInterface *); void processNewProperty(INDI::Property *); void processNewNumber(INumberVectorProperty *nvp); void processNewText(ITextVectorProperty *tvp); private slots: void changeAlwaysOnTop(Qt::ApplicationState state); void showEkosOptions(); void updateLog(); void clearLog(); void processTabChange(); void processServerTermination(const QString &host, const QString &port); void removeDevice(ISD::GDInterface *); void deviceConnected(); void deviceDisconnected(); //void processINDIModeChange(); void checkINDITimeout(); // Logs void updateDebugInterfaces(); void watchDebugProperty(ISwitchVectorProperty *svp); void setTelescope(ISD::GDInterface *); void setCCD(ISD::GDInterface *); void setFilter(ISD::GDInterface *); void setFocuser(ISD::GDInterface *); void setDome(ISD::GDInterface *); void setWeather(ISD::GDInterface *); void setDustCap(ISD::GDInterface *); void setLightBox(ISD::GDInterface *); void setST4(ISD::ST4 *); // Profiles void addProfile(); void editProfile(); void deleteProfile(); void wizardProfile(); // Mount Summary void updateMountCoords(const QString &ra, const QString &dec, const QString &az, const QString &alt); void updateMountStatus(ISD::Telescope::Status status); void setTarget(SkyObject *o); // Capture Summary void updateCaptureStatus(CaptureState status); void updateCaptureProgress(SequenceJob *job); void updateExposureProgress(SequenceJob *job); void updateCaptureCountDown(); // Focus summary void setFocusStatus(FocusState status); void updateFocusStarPixmap(QPixmap &starPixmap); void updateFocusProfilePixmap(QPixmap &profilePixmap); void updateCurrentHFR(double newHFR, int position); // Guide Summary void updateGuideStatus(GuideState status); void updateGuideStarPixmap(QPixmap &starPix); void updateGuideProfilePixmap(QPixmap &profilePix); void updateSigmas(double ra, double de); private: void removeTabs(); void reset(); void initCapture(); void initFocus(); void initGuide(); void initAlign(); void initMount(); void initDome(); void initWeather(); + void initObservatory(Weather *weather, Dome *dome); void initDustCap(); void loadDrivers(); void loadProfiles(); /** * @brief syncActiveDevices Syncs ACTIVE_DEVICES such as ACTIVE_TELESCOPE and ACTIVE_CCD * to the currently detected devices. */ void syncActiveDevices(); // Connect Signals/Slots of Ekos modules void connectModules(); // Check if INDI server is already running bool isRunning(const QString &process); // Find List of devices of specific family type QList findDevices(DeviceFamily type); ProfileInfo *getCurrentProfile(); void getCurrentProfileTelescopeInfo(double &primaryFocalLength, double &primaryAperture, double &guideFocalLength, double &guideAperture); void updateProfileLocation(ProfileInfo *pi); bool useGuideHead { false }; bool useST4 { false }; // Containers // All Drivers QHash driversList; // All managed drivers QList managedDrivers; // All generic devices (i.e. those define by INDI server) QList genericDevices; // All Managed devices (ie. those explicitly defined in the profile) QMap managedDevices; // Smart pointers for the various Ekos Modules std::unique_ptr captureProcess; std::unique_ptr focusProcess; std::unique_ptr guideProcess; std::unique_ptr alignProcess; std::unique_ptr mountProcess; std::unique_ptr schedulerProcess; + std::unique_ptr observatoryProcess; std::unique_ptr domeProcess; std::unique_ptr weatherProcess; std::unique_ptr dustCapProcess; std::unique_ptr ekosLiveClient; bool m_LocalMode { true }; bool m_isStarted { false }; bool m_RemoteManagerStart { false }; int nDevices { 0 }; QStringList m_LogText; KPageWidgetItem *ekosOptionsWidget { nullptr }; CommunicationStatus m_ekosStatus { Ekos::Idle }; CommunicationStatus m_indiStatus { Ekos::Idle }; std::unique_ptr profileModel; QList> profiles; // Filter Manager QSharedPointer filterManager; // Mount Summary QProgressIndicator *mountPI { nullptr }; // Capture Summary QTime imageCountDown; QTime overallCountDown; QTime sequenceCountDown; QTimer countdownTimer; QProgressIndicator *capturePI { nullptr }; // Preview Frame std::unique_ptr summaryPreview; // Focus Summary QProgressIndicator *focusPI { nullptr }; std::unique_ptr focusStarPixmap; // Guide Summary QProgressIndicator *guidePI { nullptr }; std::unique_ptr guideStarPixmap; ProfileInfo *currentProfile { nullptr }; bool profileWizardLaunched { false }; // Serial Port Assistant std::unique_ptr serialPortAssistant; // Logs QPointer opsLogs; friend class EkosLive::Client; friend class EkosLive::Message; friend class EkosLive::Media; }; } diff --git a/kstars/ekos/observatory/observatory.cpp b/kstars/ekos/observatory/observatory.cpp new file mode 100644 index 000000000..8332703fd --- /dev/null +++ b/kstars/ekos/observatory/observatory.cpp @@ -0,0 +1,375 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#include "observatory.h" + +#include "ekos_observatory_debug.h" + +namespace Ekos +{ +Observatory::Observatory() +{ + setupUi(this); + + // status control + mObservatoryModel = new ObservatoryModel(); + setObseratoryStatusControl(mObservatoryModel->statusControl()); + // update UI for status control + connect(useDomeCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); + connect(useShutterCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); + connect(useWeatherCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); + connect(mObservatoryModel, &Ekos::ObservatoryModel::newStatus, this, &Ekos::Observatory::observatoryStatusChanged); + // ready button deactivated + // connect(statusReadyButton, &QPushButton::clicked, mObservatoryModel, &Ekos::ObservatoryModel::makeReady); + statusReadyButton->setEnabled(false); + + setDomeModel(new ObservatoryDomeModel()); + setWeatherModel(new ObservatoryWeatherModel()); + statusDefinitionBox->setVisible(true); + statusDefinitionBox->setEnabled(true); + // make invisible, since not implemented yet + angleLabel->setVisible(false); + domeAngleSpinBox->setVisible(false); + setDomeAngleButton->setVisible(false); + weatherWarningSchedulerCB->setVisible(false); + weatherAlertSchedulerCB->setVisible(false); +} + +void Observatory::setObseratoryStatusControl(ObservatoryStatusControl control) +{ + if (mObservatoryModel != nullptr) + { + useDomeCB->setChecked(control.useDome); + useShutterCB->setChecked(control.useShutter); + useWeatherCB->setChecked(control.useWeather); + } +} + + +void Observatory::setDomeModel(ObservatoryDomeModel *model) +{ + mObservatoryModel->setDomeModel(model); + if (model != nullptr) + { + connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); + connect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); + connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); + connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); + + connect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); + connect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); + connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); + + connect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); + connect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); + connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherAlertSettingsChanged(); }); + } + else + { + shutdownDome(); + disconnect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); + disconnect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); + disconnect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); + disconnect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); + + disconnect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); + disconnect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); + connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); + + disconnect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); + disconnect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); + connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); + } +} + +void Observatory::initDome() +{ + domeBox->setEnabled(true); + + if (getDomeModel() != nullptr) + { + connect(getDomeModel(), &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText); + + if (getDomeModel()->canPark()) + { + connect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); + connect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); + domePark->setEnabled(true); + domeUnpark->setEnabled(true); + } + else + { + domePark->setEnabled(false); + domeUnpark->setEnabled(false); + } + + if (getDomeModel()->hasShutter()) + { + shutterBox->setVisible(true); + shutterBox->setEnabled(true); + connect(shutterOpen, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::openShutter); + connect(shutterClosed, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::closeShutter); + shutterClosed->setEnabled(true); + shutterOpen->setEnabled(true); + } + else + { + shutterBox->setVisible(false); + weatherWarningShutterCB->setVisible(false); + weatherAlertShutterCB->setVisible(false); + } + + setDomeStatus(getDomeModel()->status()); + setShutterStatus(getDomeModel()->shutterStatus()); + } + +} + +void Observatory::shutdownDome() +{ + domeBox->setEnabled(false); + shutterBox->setEnabled(false); + shutterBox->setVisible(false); + domePark->setEnabled(false); + domeUnpark->setEnabled(false); + shutterClosed->setEnabled(false); + shutterOpen->setEnabled(false); + angleLabel->setEnabled(false); + domeAngleSpinBox->setEnabled(false); + setDomeAngleButton->setEnabled(false); + + disconnect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); + disconnect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); +} + +void Observatory::setDomeStatus(ISD::Dome::Status status) +{ + switch (status) { + case ISD::Dome::DOME_ERROR: + break; + case ISD::Dome::DOME_IDLE: + domePark->setChecked(false); + domePark->setText("PARK"); + domeUnpark->setChecked(true); + domeUnpark->setText("UNPARKED"); + appendLogText("Dome is unparked."); + break; + case ISD::Dome::DOME_MOVING: + appendLogText("Dome is moving..."); + break; + case ISD::Dome::DOME_PARKED: + domePark->setChecked(true); + domePark->setText("PARKED"); + domeUnpark->setChecked(false); + domeUnpark->setText("UNPARK"); + appendLogText("Dome is parked."); + break; + case ISD::Dome::DOME_PARKING: + domePark->setText("PARKING"); + domeUnpark->setText("UNPARK"); + appendLogText("Dome is parking..."); + break; + case ISD::Dome::DOME_UNPARKING: + domePark->setText("PARK"); + domeUnpark->setText("UNPARKING"); + appendLogText("Dome is unparking..."); + break; + case ISD::Dome::DOME_TRACKING: + appendLogText("Dome is tracking."); + break; + default: + break; + } +} + + +void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status) +{ + switch (status) { + case ISD::Dome::SHUTTER_OPEN: + shutterOpen->setChecked(true); + shutterClosed->setChecked(false); + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSE"); + appendLogText("Shutter is open."); + break; + case ISD::Dome::SHUTTER_OPENING: + shutterOpen->setText("OPENING"); + shutterClosed->setText("CLOSED"); + appendLogText("Shutter is opening..."); + break; + case ISD::Dome::SHUTTER_CLOSED: + shutterOpen->setChecked(false); + shutterClosed->setChecked(true); + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSED"); + appendLogText("Shutter is closed."); + break; + case ISD::Dome::SHUTTER_CLOSING: + shutterOpen->setText("OPEN"); + shutterClosed->setText("CLOSING"); + appendLogText("Shutter is closing..."); + break; + default: + break; + } +} + + + + +void Observatory::setWeatherModel(ObservatoryWeatherModel *model) +{ + mObservatoryModel->setWeatherModel(model); + + if (model != nullptr) + { + connect(weatherWarningBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setWarningActionsActive); + connect(weatherAlertBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setAlertActionsActive); + + connect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); + connect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); + connect(model, &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); + connect(&weatherStatusTimer, &QTimer::timeout, [this]() + { + weatherWarningStatusLabel->setText(getWeatherModel()->getWarningActionsStatus()); + weatherAlertStatusLabel->setText(getWeatherModel()->getAlertActionsStatus()); + }); + } + else + { + shutdownWeather(); + disconnect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); + disconnect(model, &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); + disconnect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); + + disconnect(weatherWarningBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setWarningActionsActive); + disconnect(weatherAlertBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setAlertActionsActive); + } +} + +void Observatory::initWeather() +{ + weatherBox->setEnabled(true); + weatherLabel->setEnabled(true); + weatherActionsBox->setVisible(true); + weatherActionsBox->setEnabled(true); + weatherWarningBox->setChecked(getWeatherModel()->getWarningActionsActive()); + weatherAlertBox->setChecked(getWeatherModel()->getAlertActionsActive()); + setWeatherStatus(getWeatherModel()->status()); + setWarningActions(getWeatherModel()->getWarningActions()); + setAlertActions(getWeatherModel()->getAlertActions()); + weatherStatusTimer.start(1000); +} + +void Observatory::shutdownWeather() +{ + weatherBox->setEnabled(false); + weatherLabel->setEnabled(false); + setWeatherStatus(ISD::Weather::WEATHER_IDLE); + weatherStatusTimer.stop(); +} + + +void Observatory::setWeatherStatus(ISD::Weather::Status status) +{ + std::string label; + switch (status) { + case ISD::Weather::WEATHER_OK: + label = "security-high"; + appendLogText("Weather is OK."); + break; + case ISD::Weather::WEATHER_WARNING: + label = "security-medium"; + appendLogText("Weather WARNING!"); + break; + case ISD::Weather::WEATHER_ALERT: + label = "security-low"; + appendLogText("!! WEATHER ALERT !!"); + break; + default: + label = ""; + break; + } + + weatherStatusLabel->setPixmap(QIcon::fromTheme(label.c_str()) + .pixmap(QSize(48, 48))); +} + + +void Observatory::weatherWarningSettingsChanged() +{ + struct WeatherActions actions; + actions.parkDome = weatherWarningDomeCB->isChecked(); + actions.closeShutter = weatherWarningShutterCB->isChecked(); + actions.delay = weatherWarningDelaySB->value(); + + getWeatherModel()->setWarningActions(actions); +} + +void Observatory::weatherAlertSettingsChanged() +{ + struct WeatherActions actions; + actions.parkDome = weatherAlertDomeCB->isChecked(); + actions.closeShutter = weatherAlertShutterCB->isChecked(); + actions.delay = weatherAlertDelaySB->value(); + + getWeatherModel()->setAlertActions(actions); +} + +void Observatory::observatoryStatusChanged(bool ready) +{ + // statusReadyButton->setEnabled(!ready); + statusReadyButton->setChecked(ready); + emit newStatus(ready); +} + + +void Observatory::setWarningActions(WeatherActions actions) +{ + weatherWarningDomeCB->setChecked(actions.parkDome); + weatherWarningShutterCB->setChecked(actions.closeShutter); + weatherWarningDelaySB->setValue(actions.delay); +} + + +void Observatory::setAlertActions(WeatherActions actions) +{ + weatherAlertDomeCB->setChecked(actions.parkDome); + weatherAlertShutterCB->setChecked(actions.closeShutter); + weatherAlertDelaySB->setValue(actions.delay); +} + +void Observatory::statusControlSettingsChanged() +{ + ObservatoryStatusControl control; + control.useDome = useDomeCB->isChecked(); + control.useShutter = useShutterCB->isChecked(); + control.useWeather = useWeatherCB->isChecked(); + mObservatoryModel->setStatusControl(control); +} + + +void Observatory::appendLogText(const QString &text) +{ + m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", + QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text)); + + qCInfo(KSTARS_EKOS_OBSERVATORY) << text; + + emit newLog(text); +} + +void Observatory::clearLog() +{ + m_LogText.clear(); + emit newLog(QString()); +} + +} diff --git a/kstars/ekos/observatory/observatory.h b/kstars/ekos/observatory/observatory.h new file mode 100644 index 000000000..dfb116e85 --- /dev/null +++ b/kstars/ekos/observatory/observatory.h @@ -0,0 +1,90 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#pragma once + +#include "ui_observatory.h" +#include "observatorymodel.h" +#include "observatorydomemodel.h" +#include "observatoryweathermodel.h" +#include "indiweather.h" + +#include +#include +#include "klocalizedstring.h" + + +namespace Ekos +{ + +class Observatory : public QWidget, public Ui::Observatory +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Observatory") + Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) + +public: + Observatory(); + ObservatoryDomeModel *getDomeModel() { return mObservatoryModel->getDomeModel(); } + ObservatoryWeatherModel *getWeatherModel() { return mObservatoryModel->getWeatherModel(); } + + // Logging + QStringList logText() { return m_LogText; } + QString getLogText() { return m_LogText.join("\n"); } + +signals: + Q_SCRIPTABLE void newLog(const QString &text); + + /** + * @brief Signal a new observatory status + */ + Q_SCRIPTABLE void newStatus(bool isReady); + +private: + ObservatoryModel *mObservatoryModel = nullptr; + + void setDomeModel(ObservatoryDomeModel *model); + void setWeatherModel(ObservatoryWeatherModel *model); + + // Logging + QStringList m_LogText; + void appendLogText(const QString &); + void clearLog(); + + // timer for refreshing the observatory status + QTimer weatherStatusTimer; + + // reacting on weather changes + void setWarningActions(WeatherActions actions); + void setAlertActions(WeatherActions actions); + +private slots: + // observatory status handling + void setObseratoryStatusControl(ObservatoryStatusControl control); + void statusControlSettingsChanged(); + + void initWeather(); + void shutdownWeather(); + void setWeatherStatus(ISD::Weather::Status status); + + + // reacting on weather changes + void weatherWarningSettingsChanged(); + void weatherAlertSettingsChanged(); + + // reacting on observatory status changes + void observatoryStatusChanged(bool ready); + + void initDome(); + void shutdownDome(); + + void setDomeStatus(ISD::Dome::Status status); + void setShutterStatus(ISD::Dome::ShutterStatus status); +}; +} diff --git a/kstars/ekos/observatory/observatory.ui b/kstars/ekos/observatory/observatory.ui new file mode 100644 index 000000000..cc4b3151f --- /dev/null +++ b/kstars/ekos/observatory/observatory.ui @@ -0,0 +1,649 @@ + + + Observatory + + + + 0 + 0 + 904 + 536 + + + + Observatory + + + Status of the Observatory + + + + + + false + + + Observatory Status + + + + 6 + + + + + <html><head/><body><p>If selected, the shutter needs to be open for the observatory status being &quot;READY&quot;.</p></body></html> + + + Shutter + + + + + + + <html><head/><body><p>If selected, the weather needs to be OK for the observatory status being &quot;READY&quot;.</p></body></html> + + + Weather + + + + + + + <html><head/><body><p>If selected, the dome needs to be unparked for the observatory status being &quot;READY&quot;.</p></body></html> + + + Dome + + + + + + + false + + + + 96 + 36 + + + + + 72 + 36 + + + + <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + READY + + + + 32 + 16 + + + + true + + + false + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Dome + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + PARK + + + + 32 + 16 + + + + true + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + UNPARKED + + + true + + + false + + + + + + + + + false + + + Angle + + + + + + + false + + + + + + + false + + + + 72 + 24 + + + + + 72 + 24 + + + + Set + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + false + + + Weather + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Display the weather status. The warning and alert limits are set in the INDI tab. + + + Weather Status: + + + + + + + + 0 + 0 + + + + + 48 + 48 + + + + + 48 + 48 + + + + + + + + + + + false + + + Actions + + + + + + War&ning + + + true + + + + 0 + + + 0 + + + + + park dome + + + + + + + close shutter + + + + + + + stop scheduler + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> + + + Qt::AutoText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + delay (sec): + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 + + + + + + + + + + + + Ale&rt + + + true + + + + 0 + + + 0 + + + + + close shutter + + + + + + + park dome + + + + + + + stop scheduler + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> + + + Qt::AutoText + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + delay (sec): + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 + + + + + + + + + + + + + + + + + + false + + + Shutter + + + + + + false + + + + 96 + 36 + + + + + 72 + 36 + + + + <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + CLOSED + + + + 32 + 16 + + + + true + + + + + + + false + + + + 96 + 36 + + + + + 72 + 36 + + + + <html><head/><body><p>Open the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + OPEN + + + true + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/kstars/ekos/observatory/observatorydomemodel.cpp b/kstars/ekos/observatory/observatorydomemodel.cpp new file mode 100644 index 000000000..0094695b6 --- /dev/null +++ b/kstars/ekos/observatory/observatorydomemodel.cpp @@ -0,0 +1,88 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#include "observatorydomemodel.h" + +namespace Ekos +{ + +void ObservatoryDomeModel::initModel(Dome *dome) +{ + mDome = dome; + + connect(mDome, &Dome::ready, this, &ObservatoryDomeModel::ready); + connect(mDome, &Dome::disconnected, this, &ObservatoryDomeModel::disconnected); + connect(mDome, &Dome::newStatus, this, &ObservatoryDomeModel::newStatus); + connect(mDome, &Dome::newShutterStatus, this, &ObservatoryDomeModel::newShutterStatus); + +} + + +ISD::Dome::Status ObservatoryDomeModel::status() +{ + if (mDome == nullptr) + return ISD::Dome::DOME_IDLE; + + return mDome->status(); +} + +ISD::Dome::ShutterStatus ObservatoryDomeModel::shutterStatus() +{ + if (mDome == nullptr) + return ISD::Dome::SHUTTER_UNKNOWN; + + return mDome->shutterStatus(); +} + +void ObservatoryDomeModel::park() +{ + if (mDome == nullptr) + return; + + emit newLog("Parking dome..."); + mDome->park(); +} + + +void ObservatoryDomeModel::unpark() +{ + if (mDome == nullptr) + return; + + emit newLog("Unparking dome..."); + mDome->unpark(); +} + +void ObservatoryDomeModel::openShutter() +{ + if (mDome == nullptr) + return; + + emit newLog("Opening shutter..."); + mDome->controlShutter(true); +} + +void ObservatoryDomeModel::closeShutter() +{ + if (mDome == nullptr) + return; + + emit newLog("Closing shutter..."); + mDome->controlShutter(false); +} + +void ObservatoryDomeModel::execute(WeatherActions actions) +{ + if (hasShutter() && actions.closeShutter) + closeShutter(); + if (actions.parkDome) + park(); +} + +} diff --git a/kstars/ekos/observatory/observatorydomemodel.h b/kstars/ekos/observatory/observatorydomemodel.h new file mode 100644 index 000000000..3121e1b4d --- /dev/null +++ b/kstars/ekos/observatory/observatorydomemodel.h @@ -0,0 +1,57 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#pragma once + +#include "../auxiliary/dome.h" +#include "observatoryweathermodel.h" + +#include + + +namespace Ekos +{ + +class ObservatoryDomeModel: public QObject +{ + + Q_OBJECT + +public: + ObservatoryDomeModel() = default; + + void initModel(Dome *dome); + + ISD::Dome::Status status(); + ISD::Dome::ShutterStatus shutterStatus(); + + // proxies to the underlying dome object + bool canPark() { return (mDome != nullptr && mDome->canPark()); } + void park(); + void unpark(); + bool hasShutter() { return (mDome != nullptr && mDome->hasShutter()); } + void openShutter(); + void closeShutter(); + +public slots: + void execute(WeatherActions actions); + + +private: + Dome *mDome; + +signals: + void newStatus(ISD::Dome::Status state); + void newShutterStatus(ISD::Dome::ShutterStatus status); + void ready(); + void disconnected(); + void newLog(const QString &text); +}; + +} diff --git a/kstars/ekos/observatory/observatorymodel.cpp b/kstars/ekos/observatory/observatorymodel.cpp new file mode 100644 index 000000000..ede19249b --- /dev/null +++ b/kstars/ekos/observatory/observatorymodel.cpp @@ -0,0 +1,111 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#include "observatorymodel.h" +#include "Options.h" + +namespace Ekos +{ + +ObservatoryModel::ObservatoryModel() +{ + + mStatusControl.useDome = Options::observatoryStatusUseDome(); + mStatusControl.useShutter = Options::observatoryStatusUseShutter(); + mStatusControl.useWeather = Options::observatoryStatusUseWeather(); + + setDomeModel(new ObservatoryDomeModel()); + setWeatherModel(new ObservatoryWeatherModel()); +} + +void ObservatoryModel::setDomeModel(ObservatoryDomeModel *model) { + mDomeModel = model; + if (model != nullptr) + { + connect(mDomeModel, &ObservatoryDomeModel::newStatus, [this](ISD::Dome::Status s) { Q_UNUSED(s); updateStatus(); }); + connect(mDomeModel, &ObservatoryDomeModel::newShutterStatus, [this](ISD::Dome::ShutterStatus s) { Q_UNUSED(s); updateStatus(); }); + if (mWeatherModel != nullptr) + connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); + } + else + { + if (mWeatherModel != nullptr) + disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); + } + + updateStatus(); +} + +void ObservatoryModel::setWeatherModel(ObservatoryWeatherModel *model) { + mWeatherModel = model; + if (model != nullptr) + { + connect(mWeatherModel, &ObservatoryWeatherModel::newStatus, [this](ISD::Weather::Status s) { Q_UNUSED(s); updateStatus(); }); + if (mDomeModel != nullptr) + { + connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); + } + } + else + { + if (mDomeModel != nullptr) + disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); + } + updateStatus(); +} + + +void ObservatoryModel::setStatusControl(ObservatoryStatusControl control) +{ + mStatusControl = control; + Options::setObservatoryStatusUseDome(control.useDome); + Options::setObservatoryStatusUseShutter(control.useShutter); + Options::setObservatoryStatusUseWeather(control.useWeather); + updateStatus(); +} + +bool ObservatoryModel::isReady() +{ + // dome relevant for the status and dome is ready + if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->status() != ISD::Dome::DOME_IDLE)) + return false; + + // shutter relevant for the status and shutter open + if (mStatusControl.useShutter && (getDomeModel() == nullptr || + (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) + return false; + + // weather relevant for the status and weather is OK + if (mStatusControl.useWeather && (getWeatherModel() == nullptr || getWeatherModel()->status() != ISD::Weather::WEATHER_OK)) + return false; + + return true; +} + +void ObservatoryModel::updateStatus() +{ + emit newStatus(isReady()); +} + +void ObservatoryModel::makeReady() +{ + // dome relevant for the status and dome is ready + if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->status() != ISD::Dome::DOME_IDLE)) + getDomeModel()->unpark(); + + // shutter relevant for the status and shutter open + if (mStatusControl.useShutter && (getDomeModel() == nullptr || + (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) + getDomeModel()->openShutter(); + + // weather relevant for the status and weather is OK + // Haha, weather we can't change +} + +} diff --git a/kstars/ekos/observatory/observatorymodel.h b/kstars/ekos/observatory/observatorymodel.h new file mode 100644 index 000000000..98b3a81c6 --- /dev/null +++ b/kstars/ekos/observatory/observatorymodel.h @@ -0,0 +1,78 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#pragma once + +#include "observatorydomemodel.h" +#include "observatoryweathermodel.h" + +#include + +namespace Ekos +{ + +struct ObservatoryStatusControl +{ + bool useDome, useShutter, useWeather; +}; + +class ObservatoryModel : public QObject +{ + + Q_OBJECT + +public: + ObservatoryModel(); + + ObservatoryDomeModel *getDomeModel() { return mDomeModel; } + ObservatoryWeatherModel *getWeatherModel() { return mWeatherModel; } + + void setDomeModel(ObservatoryDomeModel *model); + void setWeatherModel(ObservatoryWeatherModel *model); + + /** + * @brief Retrieve the settings that define, from which states the + * "ready" state of the observatory is derived from. + */ + ObservatoryStatusControl statusControl() { return mStatusControl; } + void setStatusControl(ObservatoryStatusControl control); + + /** + * @brief Is the observatory ready? This depends upon the states of the weather, + * dome etc and upon whether these settings are relevant (see status control). + */ + bool isReady(); + +public slots: + // call this slot in case that the weather or dome status has changed + void updateStatus(); + + /** + * @brief Depending on the status control settings execute everything so + * that the status reaches the state "READY". + */ + void makeReady(); + +signals: + /** + * @brief Signal a new observatory status + * @param isReady + */ + void newStatus(bool isReady); + + +private: + ObservatoryStatusControl mStatusControl; + + ObservatoryDomeModel *mDomeModel = nullptr; + ObservatoryWeatherModel *mWeatherModel = nullptr; + +}; + +} diff --git a/kstars/ekos/observatory/observatoryweathermodel.cpp b/kstars/ekos/observatory/observatoryweathermodel.cpp new file mode 100644 index 000000000..cab0420bd --- /dev/null +++ b/kstars/ekos/observatory/observatoryweathermodel.cpp @@ -0,0 +1,156 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#include "observatoryweathermodel.h" +#include "Options.h" + +namespace Ekos +{ + +void ObservatoryWeatherModel::initModel(Weather *weather) +{ + mWeather = weather; + + // ensure that we start the timers if required + weatherChanged(status()); + + connect(mWeather, &Weather::ready, this, &ObservatoryWeatherModel::updateWeatherStatus); + connect(mWeather, &Weather::newStatus, this, &ObservatoryWeatherModel::weatherChanged); + connect(mWeather, &Weather::disconnected, this, &ObservatoryWeatherModel::disconnected); + + // read the default values + warningActionsActive = Options::warningActionsActive(); + warningActions.parkDome = Options::weatherWarningCloseDome(); + warningActions.closeShutter = Options::weatherWarningCloseShutter(); + warningActions.delay = Options::weatherWarningDelay(); + warningActions.stopScheduler = Options::weatherAlertStopScheduler(); + alertActionsActive = Options::alertActionsActive(); + alertActions.parkDome = Options::weatherAlertCloseDome(); + alertActions.closeShutter = Options::weatherAlertCloseShutter(); + alertActions.stopScheduler = Options::weatherAlertStopScheduler(); + alertActions.delay = Options::weatherAlertDelay(); + + warningTimer.setInterval(warningActions.delay * 1000); + warningTimer.setSingleShot(true); + alertTimer.setInterval(alertActions.delay * 1000); + alertTimer.setSingleShot(true); + + connect(&warningTimer, &QTimer::timeout, [this]() { execute(warningActions); }); + connect(&alertTimer, &QTimer::timeout, [this]() { execute(alertActions); }); + + if (mWeather->status() != ISD::Weather::WEATHER_IDLE) + emit ready(); +} + +ISD::Weather::Status ObservatoryWeatherModel::status() +{ + if (mWeather == nullptr) + return ISD::Weather::WEATHER_IDLE; + + return mWeather->status(); +} + +void ObservatoryWeatherModel::setWarningActionsActive(bool active) +{ + warningActionsActive = active; + Options::setWarningActionsActive(active); + + // stop warning actions if deactivated + if (!active && warningTimer.isActive()) + warningTimer.stop(); + // start warning timer if activated + else if (active && !warningTimer.isActive() && mWeather->status() == ISD::Weather::WEATHER_WARNING) + warningTimer.start(); +} + +void ObservatoryWeatherModel::setAlertActionsActive(bool active) +{ + alertActionsActive = active; + Options::setAlertActionsActive(active); + + // stop alert actions if deactivated + if (!active && alertTimer.isActive()) + alertTimer.stop(); + // start alert timer if activated + else if (active && !alertTimer.isActive() && mWeather->status() == ISD::Weather::WEATHER_ALERT) + alertTimer.start(); +} + +void ObservatoryWeatherModel::setWarningActions(WeatherActions actions) { + warningActions = actions; + Options::setWeatherWarningCloseDome(actions.parkDome); + Options::setWeatherWarningCloseShutter(actions.closeShutter); + Options::setWeatherWarningDelay(actions.delay); + warningTimer.setInterval(actions.delay * 1000); +} + + +QString ObservatoryWeatherModel::getWarningActionsStatus() +{ + if (warningTimer.isActive()) + { + QString status; + int remaining = warningTimer.remainingTime()/1000; + return status.sprintf("%02ds remaining", remaining); + } + + return "Status: inactive"; +} + +void ObservatoryWeatherModel::setAlertActions(WeatherActions actions) { + alertActions = actions; + Options::setWeatherAlertCloseDome(actions.parkDome); + Options::setWeatherAlertCloseShutter(actions.closeShutter); + Options::setWeatherAlertDelay(actions.delay); + alertTimer.setInterval(actions.delay * 1000); +} + +QString ObservatoryWeatherModel::getAlertActionsStatus() +{ + if (alertTimer.isActive()) + { + QString status; + int remaining = alertTimer.remainingTime()/1000; + return status.sprintf("%02ds remaining", remaining); + } + + return "Status: inactive"; +} + +void ObservatoryWeatherModel::updateWeatherStatus() +{ + weatherChanged(status()); + emit ready(); +} + + +void ObservatoryWeatherModel::weatherChanged(ISD::Weather::Status status) +{ + switch (status) { + case ISD::Weather::WEATHER_OK: + warningTimer.stop(); + alertTimer.stop(); + break; + case ISD::Weather::WEATHER_WARNING: + if (warningActionsActive) + warningTimer.start(); + alertTimer.stop(); + break; + case ISD::Weather::WEATHER_ALERT: + warningTimer.stop(); + if (alertActionsActive) + alertTimer.start(); + break; + default: + break; + } + emit newStatus(status); +} + +} // Ekos diff --git a/kstars/ekos/observatory/observatoryweathermodel.h b/kstars/ekos/observatory/observatoryweathermodel.h new file mode 100644 index 000000000..f3e0e9249 --- /dev/null +++ b/kstars/ekos/observatory/observatoryweathermodel.h @@ -0,0 +1,83 @@ +/* Ekos Observatory Module + Copyright (C) Wolfgang Reissenberger + + This application 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. + */ + +#pragma once + +#include "../auxiliary/weather.h" +#include "indiweather.h" + +#include + +namespace Ekos +{ + +struct WeatherActions { + bool parkDome, closeShutter, stopScheduler; + int delay; +}; + +class ObservatoryWeatherModel : public QObject +{ + + Q_OBJECT + +public: + ObservatoryWeatherModel() = default; + + void initModel(Weather *weather); + ISD::Weather::Status status(); + + /** + * @brief Actions to be taken when a weather warning occurs + */ + WeatherActions getWarningActions() { return warningActions; } + QString getWarningActionsStatus(); + void setWarningActions(WeatherActions actions); + bool getWarningActionsActive() { return warningActionsActive; } + + /** + * @brief Actions to be taken when a weather alert occurs + */ + WeatherActions getAlertActions() { return alertActions; } + QString getAlertActionsStatus(); + void setAlertActions(WeatherActions actions); + bool getAlertActionsActive() { return alertActionsActive; } + +public slots: + /** + * @brief Activate or deactivate the weather warning actions + */ + void setWarningActionsActive(bool active); + /** + * @brief Activate or deactivate the weather alert actions + */ + void setAlertActionsActive(bool active); + +private: + Weather *mWeather; + QTimer warningTimer, alertTimer; + struct WeatherActions warningActions, alertActions; + bool warningActionsActive, alertActionsActive; + +private slots: + void weatherChanged(ISD::Weather::Status status); + void updateWeatherStatus(); + +signals: + void newStatus(ISD::Weather::Status status); + void ready(); + void disconnected(); + /** + * @brief signal that actions need to be taken due to weather conditions + */ + void execute(WeatherActions actions); + +}; + +} diff --git a/kstars/indi/indidome.cpp b/kstars/indi/indidome.cpp index 38c2e4df2..0beeb9cc8 100644 --- a/kstars/indi/indidome.cpp +++ b/kstars/indi/indidome.cpp @@ -1,376 +1,486 @@ /* INDI Dome Copyright (C) 2015 Jasem Mutlaq This application 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. */ #include #include #include #include #include #include "indidome.h" #include "kstars.h" #include "clientmanager.h" namespace ISD { Dome::Dome(GDInterface *iPtr) : DeviceDecorator(iPtr) { dType = KSTARS_DOME; qRegisterMetaType("ISD::Dome::Status"); qDBusRegisterMetaType(); readyTimer.reset(new QTimer()); readyTimer.get()->setInterval(250); readyTimer.get()->setSingleShot(true); connect(readyTimer.get(), &QTimer::timeout, this, &Dome::ready); } void Dome::registerProperty(INDI::Property *prop) { if (isConnected()) readyTimer.get()->start(); if (!strcmp(prop->getName(), "DOME_PARK")) { ISwitchVectorProperty *svp = prop->getSwitch(); m_CanPark = true; if (svp) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if ((sp->s == ISS_ON) && svp->s == IPS_OK) { m_ParkStatus = PARK_PARKED; + m_Status = DOME_PARKED; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); } else if ((sp->s == ISS_OFF) && svp->s == IPS_OK) { m_ParkStatus = PARK_UNPARKED; + m_Status = DOME_IDLE; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); } } } } else if (!strcmp(prop->getName(), "ABS_DOME_POSITION")) { m_CanAbsMove = true; } else if (!strcmp(prop->getName(), "DOME_ABORT_MOTION")) { m_CanAbort = true; } + else if (!strcmp(prop->getName(), "DOME_SHUTTER")) + { + m_HasShutter = true; + } DeviceDecorator::registerProperty(prop); } void Dome::processLight(ILightVectorProperty *lvp) { DeviceDecorator::processLight(lvp); } void Dome::processNumber(INumberVectorProperty *nvp) { if (!strcmp(nvp->name, "ABS_DOME_POSITION")) { emit azimuthPositionChanged(nvp->np[0].value); } DeviceDecorator::processNumber(nvp); } void Dome::processSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP) { if (isConnected() == false && conSP->s == ISS_ON) KStars::Instance()->slotSetDomeEnabled(true); else if (isConnected() && conSP->s == ISS_OFF) { KStars::Instance()->slotSetDomeEnabled(false); m_CanAbsMove = false; m_CanPark = false; } } } else if (!strcmp(svp->name, "DOME_PARK")) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if (svp->s == IPS_ALERT) { emit newParkStatus(PARK_ERROR); // If alert, set park status to whatever it was opposite to. That is, if it was parking and failed // then we set status to unparked since it did not successfully complete parking. if (m_ParkStatus == PARK_PARKING) m_ParkStatus = PARK_UNPARKED; else if (m_ParkStatus == PARK_UNPARKING) m_ParkStatus = PARK_PARKED; emit newParkStatus(m_ParkStatus); } else if (svp->s == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING) { m_ParkStatus = PARK_PARKING; KNotification::event(QLatin1String("DomeParking"), i18n("Dome parking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_PARKING) { m_Status = DOME_PARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_BUSY && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKING) { m_ParkStatus = PARK_UNPARKING; KNotification::event(QLatin1String("DomeUnparking"), i18n("Dome unparking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_UNPARKING) { m_Status = DOME_UNPARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_OK && sp->s == ISS_ON && m_ParkStatus != PARK_PARKED) { m_ParkStatus = PARK_PARKED; KNotification::event(QLatin1String("DomeParked"), i18n("Dome parked")); emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); if (m_Status != DOME_PARKED) { m_Status = DOME_PARKED; emit newStatus(m_Status); } } else if ( (svp->s == IPS_OK || svp->s == IPS_IDLE) && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKED) { m_ParkStatus = PARK_UNPARKED; KNotification::event(QLatin1String("DomeUnparked"), i18n("Dome unparked")); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } } } else if (!strcmp(svp->name, "DOME_MOTION")) { Status lastStatus = m_Status; if (svp->s == IPS_BUSY && lastStatus != DOME_MOVING && lastStatus != DOME_PARKING && lastStatus != DOME_UNPARKING) { m_Status = DOME_MOVING; emit newStatus(m_Status); } else if (svp->s == IPS_OK && lastStatus == DOME_MOVING) { m_Status = DOME_TRACKING; emit newStatus(m_Status); } else if (svp->s == IPS_IDLE && lastStatus != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } + else if (!strcmp(svp->name, "DOME_SHUTTER")) + { + if (svp->s == IPS_ALERT) + { + emit newShutterStatus(SHUTTER_ERROR); + + // If alert, set shutter status to whatever it was opposite to. That is, if it was opening and failed + // then we set status to closed since it did not successfully complete opening. + if (m_ShutterStatus == SHUTTER_CLOSING) + m_ShutterStatus = SHUTTER_OPEN; + else if (m_ShutterStatus == SHUTTER_CLOSING) + m_ShutterStatus = SHUTTER_CLOSED; + + emit newShutterStatus(m_ShutterStatus); + } + + ShutterStatus status = shutterStatus(svp); + + switch (status) { + case SHUTTER_CLOSING: + if (m_ShutterStatus != SHUTTER_CLOSING) + { + m_ShutterStatus = SHUTTER_CLOSING; + KNotification::event(QLatin1String("ShutterClosing"), i18n("Shutter closing is in progress")); + emit newShutterStatus(m_ShutterStatus); + } + break; + case SHUTTER_OPENING: + if (m_ShutterStatus != SHUTTER_OPENING) + { + m_ShutterStatus = SHUTTER_OPENING; + KNotification::event(QLatin1String("ShutterOpening"), i18n("Shutter opening is in progress")); + emit newShutterStatus(m_ShutterStatus); + } + break; + case SHUTTER_CLOSED: + if (m_ShutterStatus != SHUTTER_CLOSED) + { + m_ShutterStatus = SHUTTER_CLOSED; + KNotification::event(QLatin1String("ShutterClosed"), i18n("Shutter closed")); + emit newShutterStatus(m_ShutterStatus); + } + break; + case SHUTTER_OPEN: + if (m_ShutterStatus != SHUTTER_OPEN) + { + m_ShutterStatus = SHUTTER_OPEN; + KNotification::event(QLatin1String("ShutterOpened"), i18n("Shutter opened")); + emit newShutterStatus(m_ShutterStatus); + } + break; + default: + break; + } + + return; + } DeviceDecorator::processSwitch(svp); } void Dome::processText(ITextVectorProperty *tvp) { DeviceDecorator::processText(tvp); } bool Dome::Abort() { if (m_CanAbort == false) return false; ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_ABORT_MOTION"); if (motionSP == nullptr) return false; ISwitch *abortSW = IUFindSwitch(motionSP, "ABORT"); if (abortSW == nullptr) return false; abortSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); return true; } bool Dome::Park() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "PARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::UnPark() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "UNPARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::isMoving() const { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_MOTION"); if (motionSP && motionSP->s == IPS_BUSY) return true; return false; } double Dome::azimuthPosition() const { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return -1; else return az->np[0].value; } bool Dome::setAzimuthPosition(double position) { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return false; az->np[0].value = position; clientManager->sendNewNumber(az); return true; } +bool Dome::ControlShutter(bool open) +{ + ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); + + if (shutterSP == nullptr) + return false; + + ISwitch *shutterSW = IUFindSwitch(shutterSP, open ? "SHUTTER_OPEN" : "SHUTTER_CLOSE"); + + if (shutterSW == nullptr) + return false; + + IUResetSwitch(shutterSP); + shutterSW->s = ISS_ON; + clientManager->sendNewSwitch(shutterSP); + + return true; +} + +Dome::ShutterStatus Dome::shutterStatus() +{ + ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); + + return shutterStatus(shutterSP); + +} + +Dome::ShutterStatus Dome::shutterStatus(ISwitchVectorProperty *svp) +{ + if (svp == nullptr) + return SHUTTER_UNKNOWN; + + ISwitch *sp = IUFindSwitch(svp, "SHUTTER_OPEN"); + if (sp == nullptr) + return SHUTTER_UNKNOWN; + + if (svp->s == IPS_ALERT) + return SHUTTER_ERROR; + else if (svp->s == IPS_BUSY) + return (sp->s == ISS_ON) ? SHUTTER_OPENING : SHUTTER_CLOSING; + else if (svp->s == IPS_OK) + return (sp->s == ISS_ON) ? SHUTTER_OPEN : SHUTTER_CLOSED; + + // this should not happen + return SHUTTER_UNKNOWN; +} + const QString Dome::getStatusString(Dome::Status status) { switch (status) { case ISD::Dome::DOME_IDLE: return i18n("Idle"); case ISD::Dome::DOME_PARKED: return i18n("Parked"); case ISD::Dome::DOME_PARKING: return i18n("Parking"); case ISD::Dome::DOME_UNPARKING: return i18n("UnParking"); case ISD::Dome::DOME_MOVING: return i18n("Moving"); case ISD::Dome::DOME_TRACKING: return i18n("Tracking"); case ISD::Dome::DOME_ERROR: return i18n("Error"); } return i18n("Error"); } } QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status& source) { argument.beginStructure(); argument << static_cast(source); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest) { int a; argument.beginStructure(); argument >> a; argument.endStructure(); dest = static_cast(a); return argument; } diff --git a/kstars/indi/indidome.h b/kstars/indi/indidome.h index 0cb55dcae..94f8dabb8 100644 --- a/kstars/indi/indidome.h +++ b/kstars/indi/indidome.h @@ -1,104 +1,126 @@ /* INDI Dome Copyright (C) 2015 Jasem Mutlaq This application 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. */ #pragma once #include #include #include "indistd.h" namespace ISD { /** * @class Dome * Focuser class handles control of INDI dome devices. Both open and closed loop (senor feedback) domes are supported. * * @author Jasem Mutlaq */ class Dome : public DeviceDecorator { Q_OBJECT - public: - explicit Dome(GDInterface *iPtr); - typedef enum - { - DOME_IDLE, - DOME_MOVING, - DOME_TRACKING, - DOME_PARKING, - DOME_UNPARKING, - DOME_PARKED, - DOME_ERROR - } Status; +public: + explicit Dome(GDInterface *iPtr); + typedef enum + { + DOME_IDLE, + DOME_MOVING, + DOME_TRACKING, + DOME_PARKING, + DOME_UNPARKING, + DOME_PARKED, + DOME_ERROR + } Status; + + typedef enum + { + SHUTTER_UNKNOWN, + SHUTTER_OPEN, + SHUTTER_CLOSED, + SHUTTER_OPENING, + SHUTTER_CLOSING, + SHUTTER_ERROR + } ShutterStatus; void processSwitch(ISwitchVectorProperty *svp) override; void processText(ITextVectorProperty *tvp) override; void processNumber(INumberVectorProperty *nvp) override; void processLight(ILightVectorProperty *lvp) override; void registerProperty(INDI::Property *prop) override; DeviceFamily getType() override { return dType; } bool canPark() const { return m_CanPark; } bool canAbsMove() const { return m_CanAbsMove; } bool canAbort() const { return m_CanAbort; } bool isParked() const { return m_ParkStatus == PARK_PARKED; } bool isMoving() const; double azimuthPosition() const; bool setAzimuthPosition(double position); + + bool hasShutter() const + { + return m_HasShutter; + } Status status() const { return m_Status; } static const QString getStatusString (Status status); - public slots: + ShutterStatus shutterStatus(); + ShutterStatus shutterStatus(ISwitchVectorProperty *svp); + +public slots: bool Abort(); bool Park(); bool UnPark(); + bool ControlShutter(bool open); signals: void newStatus(Status status); void newParkStatus(ParkStatus status); + void newShutterStatus(ShutterStatus status); void azimuthPositionChanged(double Az); void ready(); private: ParkStatus m_ParkStatus { PARK_UNKNOWN }; + ShutterStatus m_ShutterStatus { SHUTTER_UNKNOWN }; Status m_Status { DOME_IDLE }; bool m_CanAbsMove { false }; bool m_CanPark { false }; bool m_CanAbort { false }; + bool m_HasShutter { false }; std::unique_ptr readyTimer; }; } Q_DECLARE_METATYPE(ISD::Dome::Status) QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status &source); const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest); diff --git a/kstars/indi/indilistener.cpp b/kstars/indi/indilistener.cpp index 04323aee0..3eb7c04c2 100644 --- a/kstars/indi/indilistener.cpp +++ b/kstars/indi/indilistener.cpp @@ -1,473 +1,474 @@ /* INDI Listener Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application 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. Handle INDI Standard properties. */ #include "indilistener.h" #include "clientmanager.h" #include "deviceinfo.h" #include "indicap.h" #include "indiccd.h" #include "indidome.h" #include "indifilter.h" #include "indifocuser.h" #include "indilightbox.h" #include "inditelescope.h" #include "indiweather.h" #include "kstars.h" #include "Options.h" #include "auxiliary/ksnotification.h" #include #include #include #define NINDI_STD 35 /* INDI standard property used across all clients to enable interoperability. */ const char *indi_std[NINDI_STD] = { "CONNECTION", "DEVICE_PORT", "TIME_UTC", "TIME_LST", "GEOGRAPHIC_COORD", "EQUATORIAL_COORD", "EQUATORIAL_EOD_COORD", "EQUATORIAL_EOD_COORD_REQUEST", "HORIZONTAL_COORD", "TELESCOPE_ABORT_MOTION", "ON_COORD_SET", "SOLAR_SYSTEM", "TELESCOPE_MOTION_NS", "TELESCOPE_MOTION_WE", "TELESCOPE_PARK", "DOME_PARK", "GPS_REFRESH", "WEATHER_STATUS", "CCD_EXPOSURE", "CCD_TEMPERATURE", "CCD_FRAME", "CCD_FRAME_TYPE", "CCD_BINNING", "CCD_INFO", "CCD_VIDEO_STREAM", "RAW_STREAM", "IMAGE_STREAM", "FOCUS_SPEED", "FOCUS_MOTION", "FOCUS_TIMER", "FILTER_SLOT", "WATCHDOG_HEARTBEAT", "CAP_PARK", "FLAT_LIGHT_CONTROL", "FLAT_LIGHT_INTENSITY" }; INDIListener *INDIListener::_INDIListener = nullptr; INDIListener *INDIListener::Instance() { if (_INDIListener == nullptr) { _INDIListener = new INDIListener(KStars::Instance()); connect(_INDIListener, &INDIListener::newTelescope, [&]() { KStars::Instance()->slotSetTelescopeEnabled(true); }); connect(_INDIListener, &INDIListener::newDome, [&]() { KStars::Instance()->slotSetDomeEnabled(true); }); } return _INDIListener; } INDIListener::INDIListener(QObject *parent) : QObject(parent) { } INDIListener::~INDIListener() { qDeleteAll(devices); qDeleteAll(st4Devices); } bool INDIListener::isStandardProperty(const QString &name) { for (auto &item : indi_std) { if (!strcmp(name.toLatin1().constData(), item)) return true; } return false; } ISD::GDInterface *INDIListener::getDevice(const QString &name) { foreach (ISD::GDInterface *gi, devices) { if (!strcmp(gi->getDeviceName(), name.toLatin1().constData())) return gi; } return nullptr; } void INDIListener::addClient(ClientManager *cm) { qCDebug(KSTARS_INDI) << "INDIListener: Adding a new client manager to INDI listener.."; Qt::ConnectionType type = Qt::BlockingQueuedConnection; #ifdef USE_QT5_INDI type = Qt::DirectConnection; #endif clients.append(cm); connect(cm, SIGNAL(newINDIDevice(DeviceInfo*)), this, SLOT(processDevice(DeviceInfo*)), type); connect(cm, SIGNAL(removeINDIDevice(DeviceInfo*)), this, SLOT(removeDevice(DeviceInfo*)), type); connect(cm, SIGNAL(newINDIProperty(INDI::Property*)), this, SLOT(registerProperty(INDI::Property*)), type); connect(cm, SIGNAL(removeINDIProperty(INDI::Property*)), this, SLOT(removeProperty(INDI::Property*)), type); connect(cm, SIGNAL(newINDISwitch(ISwitchVectorProperty*)), this, SLOT(processSwitch(ISwitchVectorProperty*))); connect(cm, SIGNAL(newINDIText(ITextVectorProperty*)), this, SLOT(processText(ITextVectorProperty*))); connect(cm, SIGNAL(newINDINumber(INumberVectorProperty*)), this, SLOT(processNumber(INumberVectorProperty*))); connect(cm, SIGNAL(newINDILight(ILightVectorProperty*)), this, SLOT(processLight(ILightVectorProperty*))); connect(cm, SIGNAL(newINDIBLOB(IBLOB*)), this, SLOT(processBLOB(IBLOB*))); #if INDI_VERSION_MAJOR >= 1 && INDI_VERSION_MINOR >= 5 connect(cm, SIGNAL(newINDIUniversalMessage(QString)), this, SLOT(processUniversalMessage(QString))); #endif } void INDIListener::removeClient(ClientManager *cm) { qCDebug(KSTARS_INDI) << "INDIListener: Removing client manager for server" << cm->getHost() << "@" << cm->getPort(); QList::iterator it = devices.begin(); clients.removeOne(cm); while (it != devices.end()) { DriverInfo *dv = (*it)->getDriverInfo(); bool hostSource = (dv->getDriverSource() == HOST_SOURCE) || (dv->getDriverSource() == GENERATED_SOURCE); if (cm->isDriverManaged(dv)) { // If we have multiple devices per driver, we need to remove them all if (dv->getAuxInfo().value("mdpd", false).toBool() == true) { while (it != devices.end()) { if (dv->getDevice((*it)->getDeviceName()) != nullptr) { it = devices.erase(it); } else break; } } else it = devices.erase(it); cm->removeManagedDriver(dv); cm->disconnect(this); if (hostSource) return; } else ++it; } } void INDIListener::processDevice(DeviceInfo *dv) { qCDebug(KSTARS_INDI) << "INDIListener: New device" << dv->getBaseDevice()->getDeviceName(); ISD::GDInterface *gd = new ISD::GenericDevice(*dv); devices.append(gd); emit newDevice(gd); } void INDIListener::removeDevice(DeviceInfo *dv) { qCDebug(KSTARS_INDI) << "INDIListener: Removing device" << dv->getBaseDevice()->getDeviceName() << "with unique label " << dv->getDriverInfo()->getUniqueLabel(); foreach (ISD::GDInterface *gd, devices) { if (gd->getDeviceInfo() == dv) { emit deviceRemoved(gd); devices.removeOne(gd); delete (gd); } } /*foreach(ISD::GDInterface *gd, devices) { if ( (dv->getDriverInfo()->getDevices().size() > 1 && gd->getDeviceName() == dv->getBaseDevice()->getDeviceName()) || dv->getDriverInfo()->getUniqueLabel() == gd->getDeviceName() || dv->getDriverInfo()->getDriverSource() == HOST_SOURCE || dv->getDriverInfo()->getDriverSource() == GENERATED_SOURCE) { if (dv->getDriverInfo()->getUniqueLabel().contains("Query") == false) emit deviceRemoved(gd); devices.removeOne(gd); delete(gd); if (dv->getDriverInfo()->getDriverSource() != HOST_SOURCE && dv->getDriverInfo()->getDriverSource() != GENERATED_SOURCE) return; } }*/ } void INDIListener::registerProperty(INDI::Property *prop) { qCDebug(KSTARS_INDI) << "<" << prop->getDeviceName() << ">: <" << prop->getName() << ">"; foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), prop->getDeviceName())) { if (!strcmp(prop->getName(), "EQUATORIAL_EOD_COORD") || !strcmp(prop->getName(), "EQUATORIAL_COORD") || !strcmp(prop->getName(), "HORIZONTAL_COORD")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::Telescope(gd); devices.append(gd); } emit newTelescope(gd); } else if (!strcmp(prop->getName(), "CCD_EXPOSURE")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::CCD(gd); devices.append(gd); } emit newCCD(gd); } else if (!strcmp(prop->getName(), "FILTER_NAME")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::Filter(gd); devices.append(gd); } emit newFilter(gd); } else if (!strcmp(prop->getName(), "FOCUS_MOTION")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::Focuser(gd); devices.append(gd); } emit newFocuser(gd); } - else if (!strcmp(prop->getName(), "DOME_MOTION")) + else if (!strcmp(prop->getName(), "DOME_SHUTTER") || + !strcmp(prop->getName(), "DOME_MOTION")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::Dome(gd); devices.append(gd); } emit newDome(gd); } else if (!strcmp(prop->getName(), "WEATHER_STATUS")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::Weather(gd); devices.append(gd); } emit newWeather(gd); } else if (!strcmp(prop->getName(), "CAP_PARK")) { if (gd->getType() == KSTARS_UNKNOWN) { devices.removeOne(gd); gd = new ISD::DustCap(gd); devices.append(gd); } emit newDustCap(gd); } else if (!strcmp(prop->getName(), "FLAT_LIGHT_CONTROL")) { // If light box part of dust cap if (gd->getType() == KSTARS_UNKNOWN) { if (gd->getBaseDevice()->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE) { devices.removeOne(gd); gd = new ISD::DustCap(gd); devices.append(gd); emit newDustCap(gd); } // If stand-alone light box else { devices.removeOne(gd); gd = new ISD::LightBox(gd); devices.append(gd); emit newLightBox(gd); } } } if (!strcmp(prop->getName(), "TELESCOPE_TIMED_GUIDE_WE")) { ISD::ST4 *st4Driver = new ISD::ST4(gd->getBaseDevice(), gd->getDriverInfo()->getClientManager()); st4Devices.append(st4Driver); emit newST4(st4Driver); } gd->registerProperty(prop); break; } } } void INDIListener::removeProperty(INDI::Property *prop) { if (prop == nullptr) return; foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), prop->getDeviceName())) { gd->removeProperty(prop); return; } } } void INDIListener::processSwitch(ISwitchVectorProperty *svp) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), svp->device)) { gd->processSwitch(svp); break; } } } void INDIListener::processNumber(INumberVectorProperty *nvp) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), nvp->device)) { gd->processNumber(nvp); break; } } } void INDIListener::processText(ITextVectorProperty *tvp) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), tvp->device)) { gd->processText(tvp); break; } } } void INDIListener::processLight(ILightVectorProperty *lvp) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), lvp->device)) { gd->processLight(lvp); break; } } } void INDIListener::processBLOB(IBLOB *bp) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), bp->bvp->device)) { gd->processBLOB(bp); break; } } } void INDIListener::processMessage(INDI::BaseDevice *dp, int messageID) { foreach (ISD::GDInterface *gd, devices) { if (!strcmp(gd->getDeviceName(), dp->getDeviceName())) { gd->processMessage(messageID); break; } } } void INDIListener::processUniversalMessage(const QString &message) { QString displayMessage = message; // Remove timestamp info as it is not suitable for message box int colonIndex = displayMessage.indexOf(": "); if (colonIndex > 0) displayMessage = displayMessage.mid(colonIndex+2); // Special case for Alignment since it is not tied to a device if (displayMessage.startsWith("[ALIGNMENT]")) { qCDebug(KSTARS_INDI) << "AlignmentSubSystem:" << displayMessage; return; } if (Options::messageNotificationINDI()) { KNotification::event(QLatin1String("IndiServerMessage"), displayMessage+" (INDI)"); } else { KSNotification::transient(displayMessage, i18n("INDI Server Message")); } } diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg index eeb9eab6f..43678b160 100644 --- a/kstars/kstars.kcfg +++ b/kstars/kstars.kcfg @@ -1,2309 +1,2363 @@ ksutils.h The screen coordinates of the Time InfoBox. QPoint(0,0) The screen coordinates of the Focus InfoBox. QPoint(600,0) The screen coordinates of the Geographic Location InfoBox. QPoint(0,600) If true, the Time InfoBox will show only its top line of data. true If true, the Focus InfoBox will show only its top line of data. true If true, the Geographic Location InfoBox will show only its top line of data. true Toggles display of all three InfoBoxes. true Toggles display of the Time InfoBox. true Toggles display of the Focus InfoBox. true Toggles display of the Geographic Location InfoBox. true Is the Time InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 0 0 3 Is the Focus InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 1 0 3 Is the Geographic Location InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 2 0 3 Toggle display of the status bar. true Toggle display of the Horizontal coordinates of the mouse cursor in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the current epoch in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the standard epoch in the status bar. false true 1024 768 true Black Body List of the filenames of custom object catalogs. List of integers toggling display of each custom object catalog (any nonzero value indicates the objects in that catalog will be displayed). List of names for which custom catalogs are to be displayed. Names of objects entered into the find dialog are resolved using online services and stored in the database. This option also toggles the display of such resolved objects on the sky map. true 800 600 true true false Toggle display of crosshairs centered at telescope's pointed position in the KStars sky map. true Toggle display of INDI messages in the KStars statusbar. true Show INDI messages as desktop notifications instead of dialogs. false true false false The default location of saved FITS files KSUtils::getDefaultPath("fitsDir") INDI server will attempt to bind with ports starting from this port 7624 INDI server will attempt to bind with ports ending with this port 9000 List of the aliases for filter wheel slots. PATH to indiserver binary KSUtils::getDefaultPath("indiServer") false PATH to indi drivers directory KSUtils::getDefaultPath("indiDriversDir") false 320 240 false false false false false false false false false false false The City name of the current geographic location. Greenwich The Province name of the current geographic location. This is the name of the state for locations in the U. S. The Country name of the current geographic location. United Kingdom The longitude of the current geographic location, in decimal degrees. 0.0 The latitude of the current geographic location, in decimal degrees. 51.468 -10.0 0.0 Two-letter code that determines the dates on which daylight savings time begins and ends (you can view the rules by pressing the "Explain DST Rules" button in the Geographic Location window). -- If true, focus changes will cause the sky to visibly spin to the new position. Otherwise, the display will "snap" instantly to the new position. true If true, clicking on the skymap will select the closest object and highlights it. false Type of cursor when exploring the sky map. 1 The names of the currently selected field-of-view indicators. The list of defined FOV indicator names is listed in the "Settings|FOV Symbols" menu. Telrad If true, trails attached to solar system bodies will fade into the background sky color. true The right ascension of the initial focus position of the sky map, in decimal hours. This value is volatile; it is reset whenever the program shuts down. 180.0 The declination of the initial focus position of the sky map, in decimal degrees. This value is volatile; it is reset whenever the program shuts down. 45.0 The name of the object that should be centered and tracked on startup. If no object should be centered, set to "nothing". This value is volatile; it is reset whenever the program shuts down. nothing True if the skymap should track on its initial position on startup. This value is volatile; it is reset whenever the program shuts down. false Toggle whether KStars should hide some objects while the display is moving, for smoother motion. true Toggle whether constellation boundaries are hidden while the display is in motion. true Toggle whether constellation lines are hidden while the display is in motion. false Choose sky culture. 11 Toggle whether constellation names are hidden while the display is in motion. false Toggle whether the coordinate grids are hidden while the display is in motion. true Toggle whether the Milky Way contour is hidden while the display is in motion. true Toggle whether IC objects are hidden while the display is in motion. true Toggle whether Messier objects are hidden while the display is in motion. true Toggle whether NGC objects are hidden while the display is in motion. true Toggle whether extra objects are hidden while the display is in motion. true Toggle whether solar system objects are hidden while the display is in motion. false Toggle whether faint stars are hidden while the display is in motion. true Toggle whether name labels are hidden while the display is in motion. true Toggle whether asteroids are drawn in the sky map. true Toggle whether asteroid name labels are drawn in the sky map. false true Toggle whether comets are drawn in the sky map. true Toggle whether comet comas are drawn in the sky map. true Toggle whether comet name labels are drawn in the sky map. false Toggle whether supernovae are drawn in the sky map. false Toggle whether supernova name labels are drawn in the sky map. false Set magnitude limit for supernovae to be shown on the skymap. 16 Toggle supernova alerts. true Set magnitude limit for supernovae to be alerted. 13 Toggle whether constellation boundaries are drawn in the sky map. false Toggle whether constellation boundary containing the central focus point is highlighted in the sky map. false Toggle whether constellation lines are drawn in the sky map. false Toggle whether constellation art drawn in the sky map. false Toggle whether constellation name labels are drawn in the sky map. false Toggle whether deep-sky objects are drawn in the sky map. true Toggle whether the ecliptic line is drawn in the sky map. false Toggle whether the equator line is drawn in the sky map. false Coordinate grids will automatically change according to active coordinate system. true Toggle whether the equatorial coordinate grid is drawn in the sky map. false Toggle whether the horizontal coordinate grid is drawn in the sky map. false Toggle whether the local meridian line is drawn in the sky map. false Toggle whether the region below the horizon is opaque. true Toggle whether the horizon line is drawn in the sky map. true Toggle whether flags are drawn in the sky map. true Toggle whether IC objects are drawn in the sky map. false Toggle whether NGC objects are drawn in the sky map. true Toggle whether Messier objects are drawn in the sky map. true Toggle whether Messier objects are rendered as images in the sky map. true Toggle whether extra objects are drawn in the sky map. true Toggle whether the Milky Way contour is drawn in the sky map. true Toggle whether the Milky Way contour is filled. When this option is false, the Milky Way is shown as an outline. true Meta-option to control whether all major planets (and the Sun and Moon) are drawn in the sky map. true Toggle whether major planets (and the Sun and Moon) are rendered as images in the sky map. true Toggle whether major planets (and the Sun and Moon) are labeled in the sky map. true Toggle whether the Sun is drawn in the sky map. true Toggle whether the Moon is drawn in the sky map. true Toggle whether Mercury is drawn in the sky map. true Toggle whether Venus is drawn in the sky map. true Toggle whether Mars is drawn in the sky map. true Toggle whether Jupiter is drawn in the sky map. true Toggle whether Saturn is drawn in the sky map. true Toggle whether Uranus is drawn in the sky map. true Toggle whether Neptune is drawn in the sky map. true Toggle whether Pluto is drawn in the sky map. true Toggle whether stars are drawn in the sky map. true Toggle whether star magnitude (brightness) labels are shown in the sky map. false Toggle whether star name labels are shown in the sky map. true Toggle whether deep-sky object magnitude (brightness) labels are shown in the sky map. false Toggle whether deep-sky object name labels are shown in the sky map. false The timescale above which slewing mode is forced on at all times. 60 The background fill mode for the on-screen information boxes: 0="no BG"; 1="semi-transparent BG"; 2="opaque BG" 1 Algorithm for the mapping projection. 0 Use official IAU abbreviations for constellation names. false Use Latin constellation names. false Use localized constellation names (if localized names are not available, default to Latin names). true Display the sky with horizontal coordinates (when false, equatorial coordinates will be used). true Toggle whether a centered object automatically gets a name label attached. true Toggle whether a centered solar system object automatically gets a trail attached, as long as it remains centered. true Toggle whether the object under the mouse cursor gets a transient name label. true Toggle whether object positions are corrected for the effects of atmospheric refraction (only applies when horizontal coordinates are used). true Toggle whether corrections due to bending of light around the sun are taken into account false Toggle whether the sky is rendered using antialiasing. Lines and shapes are smoother with antialiasing, but rendering the screen will take more time. true The zoom level, measured in pixels per radian. 250. 250. 5000000. When zooming in or out, change zoom speed factor by this multiplier. 0.2 0.01 1.0 The faint magnitude limit for drawing asteroids. 15.0 The maximum magnitude (visibility) to filter the asteroid data download from JPL. 12.000 Controls the relative number of asteroid name labels drawn in the map. 4.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed in. 16.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed out. 5.0 When enabled, objects whose magnitudes are unknown, or not available to KStars, are drawn irrespective of the faint limits set. true Sets the density of stars in the field of view 5 The faint magnitude limit for drawing stars, when the map is in motion (only applicable if faint stars are set to be hidden while the map is in motion). 5.0 The relative density for drawing star name and magnitude labels. 2.0 The relative density for drawing deep-sky object name and magnitude labels. 5.0 If true, long names (common names) for deep-sky objects are shown in the labels. false The maximum solar distance for drawing comets. 3.0 Use experimental OpenGL backend (deprecated). false The state of the clock (running or not) true Objects in the observing list will be highlighted with a symbol in the map. true Objects in the observing list will be highlighted with a colored name label in the map. false The observing list will prefer DSS imagery while downloading imagery. true The observing list will prefer SDSS imagery while downloading imagery. false Check this if you use a large Dobsonian telescope. Sorting by percentage current altitude is an easy way of determining what objects are well-placed for observation. However, when using a large Dobsonian telescope, objects close to the zenith are hard to observe. Since tracking there corresponds to a rotation in azimuth, it is both counterintuitive and requires the observer to frequently move the ladder. The region around the zenith where this is particularly frustrating is called the Dobsonian hole. This checkbox makes the observing list consider objects present in the hole as unfit for observation. false This specifies the angular radius of the Dobsonian hole, i.e. the region where a large Dobsonian telescope cannot be pointed easily. 15.00 40.00 The name of the color scheme moonless-night.colors The method for rendering stars: 0="realistic colors"; 1="solid red"; 2="solid black"; 3="solid white"; 4="solid real colors" 0 4 The color saturation level of stars (only applicable when using "realistic colors" mode). 6 10 The color for the angular-distance measurement ruler. #FFF The background color of the on-screen information boxes. #000 The text color for the on-screen information boxes, when activated by a mouse click. #F00 The normal text color of the on-screen information boxes. #FFF The color for the constellation boundary lines. #222 The color for the constellation boundary lines. #222 The color for the constellation figure lines. #555 The color for the constellation names. #AA7 The color for the cardinal compass point labels. #002 The color for the ecliptic line. #663 The color for the equator line. #FFF The color for the equatorial coordinate grid lines. #456 The color for the horizontal coordinate grid lines. #5A3 The color for objects which have extra URL links available. #A00 The color for the horizon line and opaque ground. #5A3 The color for the local meridian line. #0059b3 The color for Messier object symbols. #0F0 The color for NGC object symbols. #066 The color for IC object symbols. #439 The color for the Milky Way contour. #123 The color for star name labels. #7AA The color for deep-sky object name labels. #7AA The color for solar system object labels. #439 The color for solar system object trails. #963 The color for the sky background. #002 The color for the artificial horizon region. #C82828 The color for telescope target symbols. #8B8 Color of visible satellites. #00FF00 Color of invisible satellites. #FF0000 Color of satellites labels. #640000 Color of supernova #FFA500 The color for user-added object labels. #439 The color for RA Guide Error bar in Ekos guide module. #00FF00 The color for DEC Guide Error bar in Ekos guide module. #00A5FF The color for solver FOV box in Ekos alignment module. #FFFF00 false Xplanet binary path KSUtils::getDefaultPath("XplanetPath") Option to use a FIFO file instead of saving to the hard disk true How long to wait for XPlanet before giving up in milliseconds 1000 How long to pause between frames in the XPlanet Animation 100 Width of xplanet window 640 Height of xplanet window 480 If true, display a label in the upper right corner. false Show local time. true Show GMT instead of local time. false Specify the text of the first line of the label. By default, it says something like "Looking at Earth". Any instances of %t will be replaced by the target name, and any instances of %o will be replaced by the origin name. Specify the point size. 12 Set the color for the label. #F00 Specify the format for the date/time label. This format string is passed to strftime(3). The default is "%c %Z", which shows the date, time, and time zone in the locale’s appropriate date and time representation. %c %Z false true false false Draw a glare around the sun with a radius of the specified value larger than the Sun. The default value is 28. 28 Place the observer above a random latitude and longitude false Place the observer above the specified longitude and latitude true Render the target body as seen from above the specified latitude (in degrees). The default value is 0. 0 Place the observer above the specified longitude (in degrees). Longitude is positive going east, negative going west (for the earth and moon), so for example Los Angeles is at -118 or 242. The default value is 0. 0 The default is no projection. Multiple bodies will not be shown if this option is specified, although shadows will still be drawn. 0 Use a file as the background image, with the planet to be superimposed upon it. This option is only meaningful with the -projection option. A color may also be supplied. false Use a file as the background image. false The path of the background image. Use a color as the background. true The color of the background. #000 A star of the specified magnitude will have a pixel brightness of 1. The default value is 10. Stars will be drawn more brightly if this number is larger. 10 If checked, use an arc file to be plotted against the background stars. false Specify an arc file to be plotted against the background stars. If checked, use a config file. false Use the specified configuration file. If checked, use kstars's FOV. false If checked, use the specified marker file. false Specify a file containing user-defined marker data to display against the background stars. If checked, write coordinates of the bounding box for each marker in a file. false Write coordinates of the bounding box for each marker to this file. If checked, use star map file to draw the background stars. false Star map file path This option is only used when creating JPEG images. The quality can range from 0 to 100. The default value is 80. 80 Toggle whether satellite tracks are drawn in the sky map. false Toggle whether satellite tracks are drawn in the sky map. false If selected, satellites will be draw like stars, otherwise, draw satellites as small colored square. false Toggle whether satellite labels are drawn in the sky map. false List of selected satellites. Checking this option causes recomputation of current equatorial coordinates from catalog coordinates (i.e. application of precession, nutation and aberration corrections) for every redraw of the map. This makes processing slower when there are many stars to handle, but is more likely to be bug free. There are known bugs in the rendering of stars when this recomputation is avoided. false The default size for DSS images downloaded from the Internet. 15.0 To include parts of the star field, we add some extra padding around DSS images of deep-sky objects. This option configures the total (both sides) padding added to either dimension of the field. 10.0 Checking this option causes KStars to generate verbose debug information for diagnostic purposes. This may cause slowdown of KStars. false Checking this option causes KStars to generate regular debug information. true Checking this option causes KStars to stop generating ANY debug information. false Checking this option causes KStars log debug messages to the default output used by the platform (e.g. Standard Error). true Checking this option causes KStars log debug messages to a log file as specified. false Log FITS Data activity. false Log INDI devices activity. false Log Ekos Capture Module activity. false Log Ekos Focus Module activity. false Log Ekos Guide Module activity. false Log Ekos Alignment Module activity. false Log Ekos Mount Module activity. false true Display all captured FITS images in a single tab instead of multiple tabs per image. true Display all captured FITS images in a single FITS Viewer window. By default each camera create its own FITS Viewer instance false Display all opened FITS images in a single FITS Viewer window. true Bring the FITSViewer window to the foreground when receiving a new image. true false !KSUtils::isHardwareLimited() false !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() KSUtils::isHardwareLimited() 4 false false 40.0 0 600 600 true false true true true false Simulators false true false true 1 Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop. 0 Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop. 90.0 false false 1 0 If guide deviation exceeds this limit, the exposure will be automatically aborted and only resumed when the deviation is within this limit. 2 If HFR deviation exceeds this limit, the autofocus routine will be automatically started. 0.5 false false false Sets the time interval before forced autofocus attempts during a capture sequence. 60 If the target hour angle exceeds this value, Ekos will command a meridian flip and if successful it will resume guiding and capture operations. false false If set, Ekos will capture a few flat images to determine the optimal exposure time to achieve the desired ADU value. 0 Maximum difference between measured and target ADU values to deem the value as acceptable. 1000 0 0 0 0 0.1 0 false false 2.5 true false 1 30 true !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() 0 KSUtils::getDefaultPath("fitsDir") Step size of the absolute focuser. The step size TICKS should be adjusted so that when the focuser moves TICKS steps, the difference in HFR is more than 0.1 pixels. Lower the value when you are close to optimal focus. 100 Wait for this many seconds after moving the focuser before capturing the next image during AutoFocus. 0 Wait for this many seconds after resuming guide. 0 The tolerance specifies the percentage difference between the current focusing position and the minimum obtained during the focusing run. Adjustment of this value is necessary to prevent the focusing algorithm from oscillating back and forth. 1 Set the maximum travel distance of an absolute focuser. 10000 Specifies gain value of CCD when performing focusing if supported by camera. 0 Set box size to select a focus star. 64 Set horizontal binning of CCD camera while in focus mode. 1 Set vertical binning of CCD camera while in focus mode. 1 true false During full field focusing, stars which are inside this percentage of the frame are filtered out of HFR calculation (default 0%). Detection algorithms may also have an inherent filter. 0.0 During full field focusing, stars which are outside this percentage of the frame are filtered out of HFR calculation (default 100%). Detection algorithms may also have an inherent filter. 100.0 false true false 0 150 0 0 1 Specifies exposure value of CCD in seconds when performing plate solving. 1 Set binning index of CCD camera while in alignment mode. Default values 0-3 corresponding to 1x1 to 4x4 binning. 4 is max binning. 4 Use rotator when performing load and slew. false Threshold between measured and FITS position angles in arcminutes to consider the load and slew operation successful. 30 0 0 1 true false false 30 0 false 1500 true true true true true 1 true 2 true true 30 false Path to astrometry.net solver location. KSUtils::getDefaultPath("AstrometrySolverBinary") false Path to astrometry.net wcsinfo location. KSUtils::getDefaultPath("AstrometryWCSInfo") false Path to astrometry.net file location. KSUtils::getDefaultPath("AstrometryConfFile") Astrometry.net Index files Location. KSUtils::getDefaultPath("AstrometryIndexFileLocation") false Key to access astrometry.net online web services. You must register with astrometry.net to obtain a key. iczikaqstszeptgs http://nova.astrometry.net true 180 -1 1.0 0 0 localhost 4400 localhost 5656 0 1000 2 false 1 false false false 3 60 10 true false false false 2 1 0 1 45 10 500 false false false 2 true true true true true true 133.33 133.33 0 0 0 0 5000 5000 100 100 0.5 2 true true false false Log Ekos Scheduler Module activity. false Sort scheduler jobs by priority and altitude. true true false false false false true false 2 true 5 30 3 0 0 0 0 0 0 7624 8624 300 1000 None false false Toggle whether the HIPS sources are drawn in the sky map. false + + + + true + + + + true + + + + false + + + + false + + + + true + + + + 600 + + + + true + + + + true + + + + true + + + + 30 + + + + true + + + + true + + + + true + + diff --git a/kstars/org.kde.kstars.Ekos.Observatory.xml b/kstars/org.kde.kstars.Ekos.Observatory.xml new file mode 100644 index 000000000..728bc580e --- /dev/null +++ b/kstars/org.kde.kstars.Ekos.Observatory.xml @@ -0,0 +1,9 @@ + + + + + + + + +