pyobs-core 2.0.0.dev23__tar.gz → 2.0.0.dev26__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/PKG-INFO +1 -1
  2. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/comm.py +12 -9
  3. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/proxy.py +29 -1
  4. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xmppclient.py +1 -1
  5. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xmppcomm.py +25 -11
  6. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/image.py +41 -1
  7. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/calibration/_ccddata_calibrator.py +2 -8
  8. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/circularmask.py +21 -14
  9. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/projected.py +3 -11
  10. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ICamera.py +1 -2
  11. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IDataSequence.py +8 -2
  12. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ISpectrograph.py +1 -2
  13. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/camerasettings.py +5 -1
  14. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/basecamera.py +22 -4
  15. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/basespectrograph.py +1 -1
  16. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/pipelinecamera.py +2 -11
  17. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/robotic/scheduler.py +9 -2
  18. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/basetelescope.py +12 -2
  19. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/scripts/script.py +7 -1
  20. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/task.py +6 -3
  21. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/task.py +7 -2
  22. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/fits.py +42 -15
  23. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/pipeline/pipeline.py +2 -21
  24. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/time.py +4 -0
  25. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyproject.toml +1 -1
  26. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/.gitignore +0 -0
  27. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/LICENSE +0 -0
  28. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/__init__.py +0 -0
  29. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/application.py +0 -0
  30. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/background_task.py +0 -0
  31. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/cli/__init__.py +0 -0
  32. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/cli/_cli.py +0 -0
  33. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/cli/pyobs.py +0 -0
  34. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/cli/pyobsd.py +0 -0
  35. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/cli/pyobsw.py +0 -0
  36. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/__init__.py +0 -0
  37. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/commlogging.py +0 -0
  38. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/dummy/__init__.py +0 -0
  39. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/dummy/dummycomm.py +0 -0
  40. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/local/__init__.py +0 -0
  41. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/local/localcomm.py +0 -0
  42. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/local/localnetwork.py +0 -0
  43. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/__init__.py +0 -0
  44. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/rpc.py +0 -0
  45. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/serializer.py +0 -0
  46. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  47. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  48. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  49. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  50. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
  51. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  52. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  53. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/__init__.py +0 -0
  54. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/badweather.py +0 -0
  55. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/event.py +0 -0
  56. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/exposurestatuschanged.py +0 -0
  57. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/filterchanged.py +0 -0
  58. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/focusfound.py +0 -0
  59. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/goodweather.py +0 -0
  60. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/log.py +0 -0
  61. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/modechanged.py +0 -0
  62. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/moduleclosed.py +0 -0
  63. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/moduleopened.py +0 -0
  64. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/motionstatuschanged.py +0 -0
  65. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/move.py +0 -0
  66. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/newimage.py +0 -0
  67. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/newspectrum.py +0 -0
  68. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/offsets.py +0 -0
  69. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/roofclosing.py +0 -0
  70. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/roofopened.py +0 -0
  71. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/taskfailed.py +0 -0
  72. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/taskfinished.py +0 -0
  73. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/taskstarted.py +0 -0
  74. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/events/testevent.py +0 -0
  75. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/__init__.py +0 -0
  76. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/__init__.py +0 -0
  77. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/altazoffsets.py +0 -0
  78. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/exptime.py +0 -0
  79. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/genericoffset.py +0 -0
  80. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/onskydistance.py +0 -0
  81. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/pixeloffsets.py +0 -0
  82. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/radecoffsets.py +0 -0
  83. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/meta/skyoffsets.py +0 -0
  84. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processor.py +0 -0
  85. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/__init__.py +0 -0
  86. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  87. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/annotation/__init__.py +0 -0
  88. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/annotation/_pillow.py +0 -0
  89. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/annotation/circle.py +0 -0
  90. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/annotation/crosshair.py +0 -0
  91. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/annotation/text.py +0 -0
  92. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/__init__.py +0 -0
  93. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  94. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  95. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  96. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  97. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  98. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  99. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/calibration/__init__.py +0 -0
  100. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  101. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/calibration/calibration.py +0 -0
  102. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/__init__.py +0 -0
  103. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  104. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  105. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/daophot.py +0 -0
  106. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/pysep.py +0 -0
  107. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/simpledisk.py +0 -0
  108. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  109. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/exptime/__init__.py +0 -0
  110. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/exptime/exptime.py +0 -0
  111. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/exptime/star.py +0 -0
  112. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/__init__.py +0 -0
  113. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  114. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/download.py +0 -0
  115. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/flip.py +0 -0
  116. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/grayscale.py +0 -0
  117. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/httpserver.py +0 -0
  118. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/normalize.py +0 -0
  119. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/save.py +0 -0
  120. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/saveimage.py +0 -0
  121. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/smooth.py +0 -0
  122. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/image/softbin.py +0 -0
  123. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/__init__.py +0 -0
  124. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/addmask.py +0 -0
  125. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  126. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/createfilename.py +0 -0
  127. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  128. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/misc/removebackground.py +0 -0
  129. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/modules/__init__.py +0 -0
  130. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  131. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/__init__.py +0 -0
  132. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/astrometry.py +0 -0
  133. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  134. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  135. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  136. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  137. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  138. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/offsets.py +0 -0
  139. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  140. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  141. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/__init__.py +0 -0
  142. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  143. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  144. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  145. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  146. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/photometry.py +0 -0
  147. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/photutil.py +0 -0
  148. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/photometry/pysep.py +0 -0
  149. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/wcs/__init__.py +0 -0
  150. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  151. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IAbortable.py +0 -0
  152. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IAcquisition.py +0 -0
  153. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IAutoFocus.py +0 -0
  154. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IAutoGuiding.py +0 -0
  155. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IAutonomous.py +0 -0
  156. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IBinning.py +0 -0
  157. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ICalibrate.py +0 -0
  158. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IConfig.py +0 -0
  159. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ICooling.py +0 -0
  160. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IData.py +0 -0
  161. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IDome.py +0 -0
  162. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IExposure.py +0 -0
  163. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IExposureTime.py +0 -0
  164. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFilters.py +0 -0
  165. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  166. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  167. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFlatField.py +0 -0
  168. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFocusModel.py +0 -0
  169. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IFocuser.py +0 -0
  170. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IGain.py +0 -0
  171. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IImageFormat.py +0 -0
  172. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IImageType.py +0 -0
  173. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IMode.py +0 -0
  174. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IModule.py +0 -0
  175. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IMotion.py +0 -0
  176. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IMultiFiber.py +0 -0
  177. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
  178. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
  179. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingAltAz.py +0 -0
  180. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingBody.py +0 -0
  181. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
  182. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
  183. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
  184. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
  185. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingRaDec.py +0 -0
  186. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IPointingSeries.py +0 -0
  187. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IReady.py +0 -0
  188. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IRoof.py +0 -0
  189. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IRotation.py +0 -0
  190. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IRunnable.py +0 -0
  191. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IRunning.py +0 -0
  192. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IScriptRunner.py +0 -0
  193. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IStartStop.py +0 -0
  194. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IStructuredConfig.py +0 -0
  195. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ISyncTarget.py +0 -0
  196. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ITelescope.py +0 -0
  197. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ITemperatures.py +0 -0
  198. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ITrackingMode.py +0 -0
  199. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/ITrackingRate.py +0 -0
  200. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IVideo.py +0 -0
  201. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IWeather.py +0 -0
  202. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/IWindow.py +0 -0
  203. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/__init__.py +0 -0
  204. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/interfaces/interface.py +0 -0
  205. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/__init__.py +0 -0
  206. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/fitsheader.py +0 -0
  207. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/fitsnamespace.py +0 -0
  208. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/follow.py +0 -0
  209. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/motionstatus.py +0 -0
  210. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/pipeline.py +0 -0
  211. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/waitformotion.py +0 -0
  212. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/mixins/weatheraware.py +0 -0
  213. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/__init__.py +0 -0
  214. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/__init__.py +0 -0
  215. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/adaptive.py +0 -0
  216. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/basevideo.py +0 -0
  217. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/dummycamera.py +0 -0
  218. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/dummyspectrograph.py +0 -0
  219. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/camera/dummyvideo.py +0 -0
  220. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/flatfield/__init__.py +0 -0
  221. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/flatfield/flatfield.py +0 -0
  222. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/flatfield/pointing.py +0 -0
  223. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/flatfield/scheduler.py +0 -0
  224. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/focus/__init__.py +0 -0
  225. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/focus/dummyautofocus.py +0 -0
  226. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/focus/focusmodel.py +0 -0
  227. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/focus/focusseries.py +0 -0
  228. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/image/__init__.py +0 -0
  229. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/image/imagewatcher.py +0 -0
  230. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/image/imagewriter.py +0 -0
  231. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/image/pipeline.py +0 -0
  232. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/image/seeing.py +0 -0
  233. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/module.py +0 -0
  234. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/__init__.py +0 -0
  235. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/_base.py +0 -0
  236. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/_baseguiding.py +0 -0
  237. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/acquisition.py +0 -0
  238. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/autoguiding.py +0 -0
  239. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  240. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/dummyguiding.py +0 -0
  241. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  242. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  243. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  244. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  245. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  246. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
  247. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/robotic/__init__.py +0 -0
  248. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/robotic/mastermind.py +0 -0
  249. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/robotic/pointing.py +0 -0
  250. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/robotic/scriptrunner.py +0 -0
  251. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/roof/__init__.py +0 -0
  252. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/roof/basedome.py +0 -0
  253. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/roof/baseroof.py +0 -0
  254. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/roof/dummyroof.py +0 -0
  255. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/__init__.py +0 -0
  256. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
  257. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
  258. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
  259. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
  260. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/test/__init__.py +0 -0
  261. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/test/standalone.py +0 -0
  262. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/__init__.py +0 -0
  263. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/dummymode.py +0 -0
  264. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/fluentlogger.py +0 -0
  265. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/httpfilecache.py +0 -0
  266. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/kiosk.py +0 -0
  267. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/matrix.py +0 -0
  268. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/stellarium.py +0 -0
  269. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/telegram.py +0 -0
  270. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/utils/trigger.py +0 -0
  271. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/weather/__init__.py +0 -0
  272. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/weather/mockweather.py +0 -0
  273. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/weather/weather.py +0 -0
  274. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/weather/weather_api.py +0 -0
  275. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/modules/weather/weather_state.py +0 -0
  276. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/object.py +0 -0
  277. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/py.typed +0 -0
  278. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/__init__.py +0 -0
  279. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/observation.py +0 -0
  280. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/__init__.py +0 -0
  281. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  282. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  283. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  284. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  285. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  286. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  287. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  288. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  289. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  290. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  291. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  292. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  293. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  294. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  295. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  296. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  297. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  298. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/random.py +0 -0
  299. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  300. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  301. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  302. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  303. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  304. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  305. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
  306. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
  307. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  308. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  309. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  310. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  311. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/targets/target.py +0 -0
  312. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  313. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/__init__.py +0 -0
  314. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  315. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  316. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  317. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  318. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/__init__.py +0 -0
  319. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/cases.py +0 -0
  320. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/conditional.py +0 -0
  321. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/parallel.py +0 -0
  322. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/selector.py +0 -0
  323. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/control/sequential.py +0 -0
  324. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  325. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  326. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  327. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  328. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/script.py +0 -0
  329. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  330. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
  331. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  332. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/scripts/utils/log.py +0 -0
  333. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/__init__.py +0 -0
  334. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/backend/__init__.py +0 -0
  335. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/backend/observationarchive.py +0 -0
  336. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/backend/taskarchive.py +0 -0
  337. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  338. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  339. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  340. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/__init__.py +0 -0
  341. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/_portal.py +0 -0
  342. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  343. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  344. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/configdb.py +0 -0
  345. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  346. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  347. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  348. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  349. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  350. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  351. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  352. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/memory/__init__.py +0 -0
  353. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  354. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  355. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/observationarchive.py +0 -0
  356. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/storage/taskarchive.py +0 -0
  357. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/taskrunner.py +0 -0
  358. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/__init__.py +0 -0
  359. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/archive/__init__.py +0 -0
  360. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/archive/archive.py +0 -0
  361. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/archive/local_archive.py +0 -0
  362. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
  363. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  364. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  365. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  366. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  367. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  368. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  369. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  370. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  371. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  372. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  373. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  374. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  375. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  376. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  377. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/__init__.py +0 -0
  378. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/average.py +0 -0
  379. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/cache.py +0 -0
  380. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/config.py +0 -0
  381. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/config_schema.py +0 -0
  382. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/coordinates.py +0 -0
  383. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/curvefit.py +0 -0
  384. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/enums.py +0 -0
  385. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/exceptions.py +0 -0
  386. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/focusseries/__init__.py +0 -0
  387. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/focusseries/base.py +0 -0
  388. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/focusseries/photometry.py +0 -0
  389. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/focusseries/projection.py +0 -0
  390. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/grids/__init__.py +0 -0
  391. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/grids/filters.py +0 -0
  392. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/grids/grid.py +0 -0
  393. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/grids/gridnode.py +0 -0
  394. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/grids/pipeline.py +0 -0
  395. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/__init__.py +0 -0
  396. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/__init__.py +0 -0
  397. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  398. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  399. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  400. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  401. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  402. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  403. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  404. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/http.py +0 -0
  405. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/influxdb.py +0 -0
  406. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/logger.py +0 -0
  407. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/logging/__init__.py +0 -0
  408. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/logging/context.py +0 -0
  409. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/logging/resolvableerror.py +0 -0
  410. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/modulegui.py +0 -0
  411. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/offsets/__init__.py +0 -0
  412. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  413. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/offsets/applyoffsets.py +0 -0
  414. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  415. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/parallel.py +0 -0
  416. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/pipeline/__init__.py +0 -0
  417. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/pipeline/night.py +0 -0
  418. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/__init__.py +0 -0
  419. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/csv.py +0 -0
  420. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/http.py +0 -0
  421. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/log.py +0 -0
  422. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/multi.py +0 -0
  423. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/publisher/publisher.py +0 -0
  424. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/serialization.py +0 -0
  425. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/shellcommand.py +0 -0
  426. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/threads/__init__.py +0 -0
  427. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/threads/lockwithabort.py +0 -0
  428. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/utils/units.py +0 -0
  429. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/version.py +0 -0
  430. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/__init__.py +0 -0
  431. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/archivefile.py +0 -0
  432. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/bufferedfile.py +0 -0
  433. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/file.py +0 -0
  434. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/filelists/__init__.py +0 -0
  435. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/filelists/filelist.py +0 -0
  436. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/filelists/testing.py +0 -0
  437. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/httpfile.py +0 -0
  438. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/localfile.py +0 -0
  439. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/memfile.py +0 -0
  440. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/sftpfile.py +0 -0
  441. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/smbfile.py +0 -0
  442. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/sshfile.py +0 -0
  443. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/tempfile.py +0 -0
  444. {pyobs_core-2.0.0.dev23 → pyobs_core-2.0.0.dev26}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev23
3
+ Version: 2.0.0.dev26
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -133,16 +133,14 @@ class Comm:
133
133
  except IndexError:
134
134
  return None
135
135
 
136
- # collect capabilities (fixed at proxy construction time)
137
- capabilities: dict[type[Interface], Any] = {}
136
+ # create new proxy
137
+ proxy = Proxy(self, client, interfaces)
138
+
139
+ # fetch capabilities in the background, so a peer that isn't fully up yet doesn't block
140
+ # proxy construction -- Proxy.wait_for_capabilities() can be used to wait for the result
138
141
  for interface in interfaces:
139
142
  if interface.capabilities is not None:
140
- cap = await self._get_capabilities(client, interface)
141
- if cap is not None:
142
- capabilities[interface] = cap
143
-
144
- # create new proxy
145
- proxy = Proxy(self, client, interfaces, capabilities)
143
+ asyncio.create_task(self._fetch_and_update_capabilities(client, interface, proxy))
146
144
 
147
145
  # subscribe to state
148
146
  for interface in interfaces:
@@ -151,9 +149,14 @@ class Comm:
151
149
 
152
150
  self._proxies[client] = proxy
153
151
 
154
- # return proxy
155
152
  return self._proxies[client]
156
153
 
154
+ async def _fetch_and_update_capabilities(self, client: str, interface: type[Interface], proxy: Proxy) -> None:
155
+ """Fetch capabilities for a single interface and push them into the given proxy once available."""
156
+ cap = await self._get_capabilities(client, interface)
157
+ if cap is not None:
158
+ proxy.update_capabilities(interface, cap)
159
+
157
160
  async def _resolve_proxy(
158
161
  self, name_or_object: str | object, obj_type: type[ProxyType] | None = None
159
162
  ) -> Any | ProxyType:
@@ -62,6 +62,7 @@ class Proxy:
62
62
  # store state and capabilities
63
63
  self._state: dict[type[Interface], Any] = {}
64
64
  self._capabilities: dict[type[Interface], Any] = capabilities if capabilities is not None else {}
65
+ self._capabilities_events: dict[type[Interface], asyncio.Event] = {}
65
66
 
66
67
  @property
67
68
  def name(self) -> str:
@@ -175,8 +176,18 @@ class Proxy:
175
176
  """Latest known state for the given interface, or None if nothing has arrived yet."""
176
177
  return self._state.get(interface)
177
178
 
179
+ def update_capabilities(self, interface: type[Interface], capabilities: Any) -> None:
180
+ """Called by Comm once capabilities for an interface arrive.
181
+
182
+ Not intended to be called directly by module code.
183
+ """
184
+ self._capabilities[interface] = capabilities
185
+ event = self._capabilities_events.get(interface)
186
+ if event is not None:
187
+ event.set()
188
+
178
189
  def get_capabilities(self, interface: type[Interface]) -> Any | None:
179
- """Capabilities for the given interface, populated once at Proxy construction."""
190
+ """Capabilities for the given interface, or None if not (yet) fetched."""
180
191
  return self._capabilities.get(interface)
181
192
 
182
193
  async def wait_for_state(
@@ -203,6 +214,23 @@ class Proxy:
203
214
 
204
215
  return self._state.get(interface)
205
216
 
217
+ async def wait_for_capabilities(
218
+ self,
219
+ interface: type[Interface],
220
+ timeout: float = 10.0,
221
+ ) -> Any | None:
222
+ """Return capabilities immediately if available, otherwise wait for the background fetch to complete."""
223
+ if self._capabilities.get(interface) is not None:
224
+ return self._capabilities[interface]
225
+
226
+ event = self._capabilities_events.setdefault(interface, asyncio.Event())
227
+ try:
228
+ await asyncio.wait_for(event.wait(), timeout=timeout)
229
+ except TimeoutError:
230
+ pass
231
+
232
+ return self._capabilities.get(interface)
233
+
206
234
 
207
235
  class _ProxyContext(Generic[ProxyType]):
208
236
  """Returned by Comm.proxy() / Object.proxy() / Comm.safe_proxy(). Must be used as:
@@ -48,7 +48,7 @@ class XmppClient(slixmpp.ClientXMPP):
48
48
  self.register_plugin("xep_0199") # XMPP Ping
49
49
 
50
50
  # enable keep alive pings
51
- self["xep_0199"].enable_keepalive()
51
+ self.plugin["xep_0199"].enable_keepalive()
52
52
 
53
53
  # handle session_start and message events
54
54
  self.add_event_handler("session_start", self.session_start)
@@ -909,20 +909,34 @@ class XmppComm(Comm):
909
909
  return self._capabilities.get(interface)
910
910
 
911
911
  async def _get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
912
- """Fetch and deserialize capabilities for a remote module's interface."""
912
+ """Fetch and deserialize capabilities for a remote module's interface.
913
+
914
+ Retries a few times, since this may run right as the peer is still starting up and hasn't
915
+ published its capabilities yet.
916
+ """
913
917
  if interface.capabilities is None:
914
918
  return None
915
- # Use full JID (with resource) if we know it — bare JID may not route correctly
916
- full_jid = next(
917
- (jid for jid in self._online_clients if jid.startswith(f"{module}@")),
918
- f"{module}@{self._domain}",
919
- )
920
- jid = full_jid
921
919
  ns = f"urn:pyobs:capabilities:{interface.__name__}:{interface.version}"
922
- try:
923
- result = await asyncio.wait_for(self.client.plugin["xep_0030"].get_info(jid=JID(jid)), timeout=10.0)
924
- except (TimeoutError, Exception) as e:
925
- log.warning("Failed to get capabilities for %s from %s: %s", interface.__name__, module, e)
920
+
921
+ result = None
922
+ last_error: BaseException | None = None
923
+ for attempt in range(3):
924
+ # Use full JID (with resource) if we know it — bare JID may not route correctly
925
+ full_jid = next(
926
+ (jid for jid in self._online_clients if jid.startswith(f"{module}@")),
927
+ f"{module}@{self._domain}",
928
+ )
929
+ try:
930
+ result = await asyncio.wait_for(
931
+ self.client.plugin["xep_0030"].get_info(jid=JID(full_jid)), timeout=10.0
932
+ )
933
+ break
934
+ except (TimeoutError, Exception) as e:
935
+ last_error = e
936
+ if attempt < 2:
937
+ await asyncio.sleep(2)
938
+ else:
939
+ log.warning("Failed to get capabilities for %s from %s: %s", interface.__name__, module, last_error)
926
940
  return None
927
941
  log.debug("get_capabilities disco result XML: %s", ET.tostring(result.xml).decode()[:500])
928
942
  # result.xml is the <iq> — the <query> is its child, capabilities are grandchildren
@@ -13,7 +13,7 @@ from astropy.table import Table
13
13
  from numpy.typing import NDArray
14
14
 
15
15
  import pyobs.utils.exceptions as exc
16
- from pyobs.utils.fits import FilenameFormatter
16
+ from pyobs.utils.fits import FilenameFormatter, parse_section_bounds
17
17
 
18
18
  MetaClass = TypeVar("MetaClass")
19
19
 
@@ -378,6 +378,46 @@ class Image:
378
378
  unit="adu",
379
379
  )
380
380
 
381
+ def trim(self) -> Image:
382
+ """Return a copy of this image cropped to its TRIMSEC header, if present.
383
+
384
+ If no TRIMSEC is set, returns an unmodified copy. CRPIX1/CRPIX2 are shifted to
385
+ account for the new origin if present. TRIMSEC/DATASEC/BIASSEC are removed from
386
+ the result's header, since none remain valid coordinates into the trimmed data.
387
+
388
+ Raises:
389
+ ValueError: If TRIMSEC is malformed, or a catalog is already attached (its
390
+ pixel coordinates would silently go stale against the trimmed frame).
391
+ """
392
+
393
+ if self._catalog is not None:
394
+ raise ValueError("Cannot trim an image with an attached catalog.")
395
+
396
+ bounds = parse_section_bounds(self._header, "TRIMSEC")
397
+ trimmed = self.copy()
398
+ if bounds is None:
399
+ return trimmed
400
+
401
+ x0, x1, y0, y1 = bounds
402
+ trimmed._data = None if self._data is None else self._data[y0:y1, x0:x1]
403
+ trimmed._mask = None if self._mask is None else self._mask[y0:y1, x0:x1]
404
+ trimmed._uncertainty = None if self._uncertainty is None else self._uncertainty[y0:y1, x0:x1]
405
+
406
+ if trimmed._data is not None:
407
+ trimmed._header["NAXIS1"] = trimmed._data.shape[1]
408
+ trimmed._header["NAXIS2"] = trimmed._data.shape[0]
409
+
410
+ if "CRPIX1" in trimmed._header:
411
+ trimmed._header["CRPIX1"] -= x0
412
+ if "CRPIX2" in trimmed._header:
413
+ trimmed._header["CRPIX2"] -= y0
414
+
415
+ for keyword in ("TRIMSEC", "DATASEC", "BIASSEC"):
416
+ if keyword in trimmed._header:
417
+ del trimmed._header[keyword]
418
+
419
+ return trimmed
420
+
381
421
  def format_filename(self, formatter: FilenameFormatter) -> str:
382
422
  """Format filename with given formatter."""
383
423
  self._header["FNAME"] = formatter(self._header)
@@ -4,17 +4,16 @@ import astropy.units as u
4
4
  from astropy.nddata import CCDData
5
5
 
6
6
  from pyobs.images import Image
7
- from pyobs.utils.pipeline import Pipeline
8
7
 
9
8
 
10
9
  class _CCDDataCalibrator:
11
10
  def __init__(self, image: Image, bias: Image | None = None, dark: Image | None = None, flat: Image | None = None):
12
- self._image = image
11
+ self._image = image.trim()
13
12
  self._bias = self._optional_to_ccddata(bias)
14
13
  self._dark = self._optional_to_ccddata(dark)
15
14
  self._flat = self._optional_to_ccddata(flat)
16
15
 
17
- self._ccd_data = image.to_ccddata()
16
+ self._ccd_data = self._image.to_ccddata()
18
17
 
19
18
  if dark is not None:
20
19
  self._dark_exp_time = dark.header["EXPTIME"]
@@ -26,16 +25,11 @@ class _CCDDataCalibrator:
26
25
  return image.to_ccddata()
27
26
 
28
27
  def __call__(self) -> Image:
29
- self._trim_image()
30
-
31
28
  calibrated_ccd_data = self._calibrate_image()
32
29
 
33
30
  calibrated = Image.from_ccddata(calibrated_ccd_data)
34
31
  return calibrated
35
32
 
36
- def _trim_image(self) -> None:
37
- self._ccd_data = Pipeline.trim_ccddata(self._ccd_data)
38
-
39
33
  def _calibrate_image(self) -> CCDData:
40
34
  import ccdproc
41
35
 
@@ -17,8 +17,9 @@ class CircularMask(ImageProcessor):
17
17
 
18
18
  This processor reads the circle center from two FITS header keywords
19
19
  (e.g., CRPIX1/CRPIX2) and constructs a circular mask in pixel coordinates. Pixels
20
- outside the circle are set to zero by in-place multiplication; pixels inside the
21
- circle are preserved. The modified image is returned.
20
+ outside the circle are flagged in ``image.mask``; pixel data are left untouched. Any
21
+ pre-existing mask on the image is combined (logical OR) with the new one rather than
22
+ being overwritten.
22
23
 
23
24
  :param float radius: Radius of the circular pass region in pixels. Pixels with
24
25
  squared distance to the center less than or equal to
@@ -33,17 +34,19 @@ class CircularMask(ImageProcessor):
33
34
  --------
34
35
  - Reads the circle center from ``image.header[center[0]]`` and
35
36
  ``image.header[center[1]]``.
36
- - Builds a boolean circular mask on the 2D pixel grid and applies it to
37
- ``image.data`` by element-wise multiplication, zeroing pixels outside the
38
- circle and keeping those inside (boundary inclusive).
39
- - Returns the same image object with modified pixel data; the FITS header and
37
+ - Builds a boolean mask on the 2D pixel grid that is ``True`` outside the circle
38
+ and ``False`` inside it (boundary inclusive), and assigns it to a copy of the
39
+ image's ``mask``. If the image already has a mask, the new mask is combined with
40
+ it via logical OR instead of replacing it.
41
+ - Returns a copy of the image with the updated mask; pixel data, header, and
40
42
  catalog are not changed.
41
43
 
42
44
  Input/Output
43
45
  ------------
44
46
  - Input: :class:`pyobs.images.Image` with 2D pixel data and FITS header containing
45
47
  the specified center keywords.
46
- - Output: :class:`pyobs.images.Image` with pixel data masked outside the circle.
48
+ - Output: :class:`pyobs.images.Image` (copied) with pixels outside the circle
49
+ marked in ``mask``; pixel data are unchanged.
47
50
 
48
51
  Configuration (YAML)
49
52
  --------------------
@@ -70,8 +73,6 @@ class CircularMask(ImageProcessor):
70
73
  ensure consistency to avoid off-by-one shifts.
71
74
  - The implementation operates on 2D images. Multi-plane/color images are not
72
75
  supported by this processor as written.
73
- - Masking is performed in place; if you need to preserve the original image data,
74
- copy the image before applying this processor.
75
76
  """
76
77
 
77
78
  __module__ = "pyobs.images.processors.misc"
@@ -90,13 +91,13 @@ class CircularMask(ImageProcessor):
90
91
  self._radius = radius
91
92
 
92
93
  async def __call__(self, image: Image) -> Image:
93
- """Remove everything outside the given radius from the image.
94
+ """Mask everything outside the given radius from the image.
94
95
 
95
96
  Args:
96
97
  image: Image to mask.
97
98
 
98
99
  Returns:
99
- Masked Image.
100
+ Image with pixels outside the circle marked in its mask.
100
101
  """
101
102
 
102
103
  center_x, center_y = image.header[self._center[0]], image.header[self._center[1]]
@@ -105,10 +106,16 @@ class CircularMask(ImageProcessor):
105
106
  x, y = np.arange(0, nx), np.arange(0, ny)
106
107
  x_coordinates, y_coordinates = np.meshgrid(x, y)
107
108
 
108
- circ_mask = (x_coordinates - center_x) ** 2 + (y_coordinates - center_y) ** 2 <= self._radius**2
109
+ outside_circle = (x_coordinates - center_x) ** 2 + (y_coordinates - center_y) ** 2 > self._radius**2
110
+ outside_circle = outside_circle.transpose()
109
111
 
110
- image.data *= circ_mask.transpose()
111
- return image
112
+ output_image = image.copy()
113
+ if output_image.safe_mask is not None:
114
+ output_image.mask = np.logical_or(output_image.mask, outside_circle)
115
+ else:
116
+ output_image.mask = outside_circle
117
+
118
+ return output_image
112
119
 
113
120
 
114
121
  __all__ = ["CircularMask"]
@@ -1,5 +1,4 @@
1
1
  import logging
2
- import re
3
2
  from typing import Any, cast
4
3
 
5
4
  import numpy as np
@@ -9,6 +8,7 @@ from scipy.optimize import fmin
9
8
 
10
9
  from pyobs.images import Image
11
10
  from pyobs.images.meta import PixelOffsets
11
+ from pyobs.utils.fits import fitssec
12
12
 
13
13
  from .offsets import Offsets
14
14
 
@@ -155,16 +155,8 @@ class ProjectedOffsets(Offsets):
155
155
  Projected images.
156
156
  """
157
157
 
158
- # get image data and header
159
- data, hdr = image.data, image.header
160
-
161
- # trimsec
162
- if "TRIMSEC" in hdr:
163
- m = re.match(r"\[([0-9]+):([0-9]+),([0-9]+):([0-9]+)\]", hdr["TRIMSEC"])
164
- if m is None:
165
- raise ValueError("Invalid trimsec.")
166
- x0, x1, y0, y1 = [int(f) for f in m.groups()]
167
- data = data[y0 - 1 : y1, x0 - 1 : x1]
158
+ # trim to trimsec, if set
159
+ data = fitssec(image)
168
160
 
169
161
  # collapse
170
162
  sum_x = np.nansum(data, 0)
@@ -1,10 +1,9 @@
1
1
  from abc import ABCMeta
2
2
 
3
3
  from .IData import IData
4
- from .IExposure import IExposure
5
4
 
6
5
 
7
- class ICamera(IData, IExposure, metaclass=ABCMeta):
6
+ class ICamera(IData, metaclass=ABCMeta):
8
7
  """The module controls a camera."""
9
8
 
10
9
  __module__ = "pyobs.interfaces"
@@ -2,8 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from abc import ABCMeta, abstractmethod
4
4
  from dataclasses import dataclass, field
5
- from typing import Any
5
+ from typing import Annotated, Any
6
6
 
7
+ from ..utils.enums import Unit
7
8
  from ..utils.time import Time
8
9
  from .IAbortable import IAbortable
9
10
 
@@ -23,13 +24,18 @@ class IDataSequence(IAbortable, metaclass=ABCMeta):
23
24
  state = DataSequenceState
24
25
 
25
26
  @abstractmethod
26
- async def grab_sequence(self, count: int, broadcast: bool = True, **kwargs: Any) -> None:
27
+ async def grab_sequence(
28
+ self, count: int, broadcast: bool = True, delay: Annotated[float, Unit.SECONDS] = 0, **kwargs: Any
29
+ ) -> None:
27
30
  """Start a sequence of `count` grabs. Returns immediately; progress is available via
28
31
  the pushed DataSequenceState.
29
32
 
30
33
  Args:
31
34
  count: Number of grabs to take.
32
35
  broadcast: Broadcast existence of each grab.
36
+ delay: Seconds to wait between the end of one grab and the start of the next.
37
+ Does not apply after the last grab. Skipped early if the sequence is aborted
38
+ during the wait.
33
39
 
34
40
  Raises:
35
41
  GrabImageError: If the device is already busy (exposing or already running a
@@ -1,10 +1,9 @@
1
1
  from abc import ABCMeta
2
2
 
3
3
  from .IData import IData
4
- from .IExposure import IExposure
5
4
 
6
5
 
7
- class ISpectrograph(IData, IExposure, metaclass=ABCMeta):
6
+ class ISpectrograph(IData, metaclass=ABCMeta):
8
7
  """The module controls a camera."""
9
8
 
10
9
  __module__ = "pyobs.interfaces"
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
  from typing import Any, cast
5
5
 
6
+ from pyobs.comm.proxy import Proxy
6
7
  from pyobs.interfaces import IBinning, IData, IFilters, IWindow
7
8
  from pyobs.modules import Module
8
9
 
@@ -54,7 +55,10 @@ class CameraSettingsMixin:
54
55
  await camera.set_binning(self.__camerasettings_binning, self.__camerasettings_binning)
55
56
  if isinstance(camera, IWindow):
56
57
  log.info("Set window to full frame...")
57
- cap = camera.get_capabilities(IWindow)
58
+ if isinstance(camera, Proxy):
59
+ cap = await camera.wait_for_capabilities(IWindow)
60
+ else:
61
+ cap = camera.get_capabilities(IWindow)
58
62
  if cap is not None:
59
63
  await camera.set_window(cap.full_frame_x, cap.full_frame_y, cap.full_frame_width, cap.full_frame_height)
60
64
  else:
@@ -46,7 +46,9 @@ async def calc_expose_timeout(camera: BaseCamera, *args: Any, **kwargs: Any) ->
46
46
  return camera._exposure_time + 30
47
47
 
48
48
 
49
- class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageType, IDataSequence, metaclass=ABCMeta):
49
+ class BaseCamera(
50
+ Module, ImageFitsHeaderMixin, ICamera, IExposure, IExposureTime, IImageType, IDataSequence, metaclass=ABCMeta
51
+ ):
50
52
  """Base class for all camera modules."""
51
53
 
52
54
  __module__ = "pyobs.modules.camera"
@@ -108,6 +110,7 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
108
110
  # (also between individual grabs, while _camera_status is briefly IDLE again)
109
111
  self._sequence_count_left = 0
110
112
  self._sequence_task: asyncio.Task[None] | None = None
113
+ self._sequence_delay_abort = asyncio.Event()
111
114
 
112
115
  # register exception
113
116
  exc.register_exception(exc.GrabImageError, 3, timespan=600, callback=self._default_remote_error_callback)
@@ -370,19 +373,23 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
370
373
  # return filename
371
374
  return filename
372
375
 
373
- async def grab_sequence(self, count: int, broadcast: bool = True, **kwargs: Any) -> None:
376
+ async def grab_sequence(self, count: int, broadcast: bool = True, delay: float = 0, **kwargs: Any) -> None:
374
377
  """Start a sequence of `count` images. Returns immediately; progress is available via
375
378
  the pushed DataSequenceState.
376
379
 
377
380
  Args:
378
381
  count: Number of images to take.
379
382
  broadcast: Broadcast existence of each image.
383
+ delay: Seconds to wait between the end of one image and the start of the next.
384
+ Does not apply after the last image.
380
385
 
381
386
  Raises:
382
387
  CameraException: If camera is already busy (exposing or already running a sequence).
383
388
  """
384
389
  if count < 1:
385
390
  raise ValueError("count must be >= 1.")
391
+ if delay < 0:
392
+ raise ValueError("delay must be >= 0.")
386
393
 
387
394
  # already running a sequence, or mid-exposure outside of one?
388
395
  if self._sequence_count_left > 0 or self._camera_status != ExposureStatus.IDLE:
@@ -391,9 +398,9 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
391
398
  log.info("Starting sequence of %d images...", count)
392
399
  self._sequence_count_left = count
393
400
  await self.comm.set_state(IDataSequence, DataSequenceState(count_total=count, count_left=count))
394
- self._sequence_task = asyncio.create_task(self._run_sequence(count, broadcast))
401
+ self._sequence_task = asyncio.create_task(self._run_sequence(count, broadcast, delay))
395
402
 
396
- async def _run_sequence(self, count_total: int, broadcast: bool) -> None:
403
+ async def _run_sequence(self, count_total: int, broadcast: bool, delay: float) -> None:
397
404
  """Runs a sequence of grab_data() calls, started by grab_sequence()."""
398
405
  try:
399
406
  while self._sequence_count_left > 0:
@@ -406,6 +413,15 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
406
413
  await self.comm.set_state(
407
414
  IDataSequence, DataSequenceState(count_total=count_total, count_left=self._sequence_count_left)
408
415
  )
416
+
417
+ # wait between images, unless this was the last one or the sequence was
418
+ # aborted in the meantime -- either abort_sequence() or abort() cuts this short
419
+ if self._sequence_count_left > 0 and delay > 0:
420
+ self._sequence_delay_abort.clear()
421
+ try:
422
+ await asyncio.wait_for(self._sequence_delay_abort.wait(), timeout=delay)
423
+ except TimeoutError:
424
+ pass
409
425
  finally:
410
426
  log.info("Finished sequence.")
411
427
  self._sequence_count_left = 0
@@ -418,6 +434,7 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
418
434
  """
419
435
  log.info("Aborting sequence after current image...")
420
436
  self._sequence_count_left = 0
437
+ self._sequence_delay_abort.set()
421
438
 
422
439
  async def _abort_exposure(self) -> None:
423
440
  """Abort the running exposure. Should be implemented by derived class.
@@ -438,6 +455,7 @@ class BaseCamera(Module, ImageFitsHeaderMixin, ICamera, IExposureTime, IImageTyp
438
455
  log.info("Aborting current image and sequence...")
439
456
  self.expose_abort.set()
440
457
  self._sequence_count_left = 0
458
+ self._sequence_delay_abort.set()
441
459
 
442
460
  # do camera-specific abort
443
461
  await self._abort_exposure()
@@ -23,7 +23,7 @@ class ExposureInfo(NamedTuple):
23
23
  start: datetime
24
24
 
25
25
 
26
- class BaseSpectrograph(Module, SpectrumFitsHeaderMixin, ISpectrograph, metaclass=ABCMeta):
26
+ class BaseSpectrograph(Module, SpectrumFitsHeaderMixin, ISpectrograph, IExposure, metaclass=ABCMeta):
27
27
  """Base class for all spectrograph modules."""
28
28
 
29
29
  __module__ = "pyobs.modules.camera"
@@ -3,11 +3,11 @@ from typing import Any
3
3
 
4
4
  from pyobs.events import NewImageEvent
5
5
  from pyobs.images import Image
6
- from pyobs.interfaces import ExposureState, ICamera, IExposure
6
+ from pyobs.interfaces import ICamera
7
7
  from pyobs.mixins import ImageFitsHeaderMixin
8
8
  from pyobs.mixins.pipeline import PipelineMixin
9
9
  from pyobs.modules import Module
10
- from pyobs.utils.enums import ExposureStatus, ImageType
10
+ from pyobs.utils.enums import ImageType
11
11
  from pyobs.utils.exceptions import GrabImageError
12
12
 
13
13
  log = logging.getLogger(__name__)
@@ -24,15 +24,6 @@ class PipelineCamera(Module, ICamera, ImageFitsHeaderMixin, PipelineMixin):
24
24
  PipelineMixin.__init__(self, **kwargs)
25
25
  ImageFitsHeaderMixin.__init__(self, **kwargs)
26
26
 
27
- async def open(self) -> None:
28
- """Open module."""
29
- await Module.open(self)
30
-
31
- # publish initial state
32
- await self.comm.set_state(
33
- IExposure, ExposureState(status=ExposureStatus.IDLE, progress=0.0, exposure_time_left=0.0)
34
- )
35
-
36
27
  async def grab_data(self, broadcast: bool = True, **kwargs: Any) -> str:
37
28
  """Grabs an image and returns reference.
38
29
 
@@ -281,14 +281,21 @@ class Scheduler(Module, IStartStop, IRunnable):
281
281
  # sleep a little
282
282
  await asyncio.sleep(1)
283
283
 
284
- @staticmethod
285
- def _log_scheduled_task(scheduled_tasks: ObservationList) -> None:
284
+ def _log_scheduled_task(self, scheduled_tasks: ObservationList) -> None:
285
+ try:
286
+ observer = self._scheduler.observer
287
+ except AttributeError:
288
+ observer = None
286
289
  for scheduled_task in scheduled_tasks:
287
290
  msg = f" - {scheduled_task.start.strftime('%H:%M:%S')} to {scheduled_task.end.strftime('%H:%M:%S')}: "
288
291
  msg += f"{scheduled_task.task.name} ({scheduled_task.task.id}"
289
292
  if scheduled_task.priority is not None:
290
293
  msg += f", priority: {scheduled_task.priority}"
291
294
  msg += ")"
295
+ if scheduled_task.target is not None and observer is not None:
296
+ start = Time(scheduled_task.start)
297
+ altaz = observer.altaz(start, scheduled_task.target.coordinates(start))
298
+ msg += f" [{scheduled_task.target.name}: alt={altaz.alt.degree:.1f}°, airmass={float(altaz.secz):.2f}]"
292
299
  log.info(msg)
293
300
 
294
301
  async def run(self, **kwargs: Any) -> None:
@@ -308,7 +308,14 @@ class BaseTelescope(
308
308
 
309
309
  # check altitude
310
310
  if alt_az.alt.degree < self._min_altitude:
311
- raise ValueError("Destination altitude below limit.")
311
+ raise ValueError(
312
+ f"Destination altitude below limit: alt={alt_az.alt.degree:.2f}° "
313
+ f"az={alt_az.az.degree:.2f}° (min={self._min_altitude:.2f}°) for "
314
+ f"ra={ra:.5f}° dec={dec:.5f}° at {Time.now().isot} from "
315
+ f"lon={self.observer.location.lon.degree:.4f}° "
316
+ f"lat={self.observer.location.lat.degree:.4f}° "
317
+ f"height={self.observer.location.height.value:.1f}m."
318
+ )
312
319
 
313
320
  # acquire lock
314
321
  async with LockWithAbort(self._lock_moving, self._abort_move):
@@ -382,7 +389,10 @@ class BaseTelescope(
382
389
 
383
390
  # check altitude
384
391
  if alt < self._min_altitude:
385
- raise ValueError("Destination altitude below limit.")
392
+ raise ValueError(
393
+ f"Destination altitude below limit: alt={alt:.2f}° az={az:.2f}° "
394
+ f"(min={self._min_altitude:.2f}°) at {Time.now().isot}."
395
+ )
386
396
 
387
397
  # acquire lock
388
398
  async with LockWithAbort(self._lock_moving, self._abort_move):
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import logging
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
+ from pydantic import PrivateAttr
7
+
6
8
  from pyobs.interfaces import FitsHeaderEntry
7
9
  from pyobs.robotic.scripts import Script
8
10
  from pyobs.robotic.storage.lco._portal import LcoRequest
@@ -24,6 +26,8 @@ class LcoScript(Script):
24
26
  request: LcoRequest
25
27
  scripts: dict[str, dict[str, Any]] = {}
26
28
 
29
+ _running_script: Script | None = PrivateAttr(default=None)
30
+
27
31
  def _create_script(self) -> Script:
28
32
  """Build the script selected via the configuration's extra_params["script_name"].
29
33
 
@@ -55,6 +59,7 @@ class LcoScript(Script):
55
59
  InterruptedError: If interrupted
56
60
  """
57
61
  script = self._create_script()
62
+ self._running_script = script
58
63
  try:
59
64
  await script.run(data)
60
65
  finally:
@@ -69,7 +74,8 @@ class LcoScript(Script):
69
74
  Returns:
70
75
  Dictionary containing FITS headers.
71
76
  """
72
- return self._create_script().get_fits_headers(namespaces)
77
+ script = self._running_script if self._running_script is not None else self._create_script()
78
+ return script.get_fits_headers(namespaces)
73
79
 
74
80
  def estimate_duration(self, data: TaskData | None = None, time: Time | None = None) -> float:
75
81
  """Estimate duration based on the duration calculated by the LCO portal."""