pyobs-core 2.0.2__tar.gz → 2.1.0__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 (453) hide show
  1. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/PKG-INFO +1 -1
  2. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xmppclient.py +0 -9
  3. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xmppcomm.py +92 -41
  4. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/taskfailed.py +9 -3
  5. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/taskfinished.py +8 -2
  6. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/taskstarted.py +12 -3
  7. pyobs_core-2.1.0/pyobs/interfaces/IRobotic.py +60 -0
  8. pyobs_core-2.1.0/pyobs/interfaces/IRoboticScheduler.py +40 -0
  9. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/__init__.py +7 -0
  10. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/dummycamera.py +3 -1
  11. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/robotic/__init__.py +3 -1
  12. pyobs_core-2.1.0/pyobs/modules/robotic/dummymastermind.py +178 -0
  13. pyobs_core-2.1.0/pyobs/modules/robotic/dummyscheduler.py +155 -0
  14. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/robotic/mastermind.py +85 -12
  15. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/robotic/scheduler.py +50 -2
  16. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyproject.toml +1 -1
  17. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/.gitignore +0 -0
  18. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/LICENSE +0 -0
  19. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/__init__.py +0 -0
  20. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/application.py +0 -0
  21. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/background_task.py +0 -0
  22. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/cli/__init__.py +0 -0
  23. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/cli/_cli.py +0 -0
  24. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/cli/pyobs.py +0 -0
  25. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/cli/pyobsd.py +0 -0
  26. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/cli/pyobsw.py +0 -0
  27. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/__init__.py +0 -0
  28. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/comm.py +0 -0
  29. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/commlogging.py +0 -0
  30. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/dummy/__init__.py +0 -0
  31. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/dummy/dummycomm.py +0 -0
  32. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/local/__init__.py +0 -0
  33. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/local/localcomm.py +0 -0
  34. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/local/localnetwork.py +0 -0
  35. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/proxy.py +0 -0
  36. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/__init__.py +0 -0
  37. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/rpc.py +0 -0
  38. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/serializer.py +0 -0
  39. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  40. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  41. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  42. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  43. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
  44. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  45. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  46. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/__init__.py +0 -0
  47. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/badweather.py +0 -0
  48. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/event.py +0 -0
  49. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/exposurestatuschanged.py +0 -0
  50. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/filterchanged.py +0 -0
  51. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/focusfound.py +0 -0
  52. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/goodweather.py +0 -0
  53. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/log.py +0 -0
  54. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/modechanged.py +0 -0
  55. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/moduleclosed.py +0 -0
  56. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/moduleopened.py +0 -0
  57. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/motionstatuschanged.py +0 -0
  58. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/move.py +0 -0
  59. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/newimage.py +0 -0
  60. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/newspectrum.py +0 -0
  61. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/offsets.py +0 -0
  62. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/roofclosing.py +0 -0
  63. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/roofopened.py +0 -0
  64. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/events/testevent.py +0 -0
  65. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/__init__.py +0 -0
  66. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/image.py +0 -0
  67. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/__init__.py +0 -0
  68. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/altazoffsets.py +0 -0
  69. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/exptime.py +0 -0
  70. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/genericoffset.py +0 -0
  71. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/onskydistance.py +0 -0
  72. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/pixeloffsets.py +0 -0
  73. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/radecoffsets.py +0 -0
  74. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/meta/skyoffsets.py +0 -0
  75. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processor.py +0 -0
  76. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/__init__.py +0 -0
  77. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  78. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/annotation/__init__.py +0 -0
  79. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/annotation/_pillow.py +0 -0
  80. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/annotation/circle.py +0 -0
  81. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/annotation/crosshair.py +0 -0
  82. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/annotation/text.py +0 -0
  83. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/__init__.py +0 -0
  84. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  85. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  86. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  87. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  88. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  89. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  90. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/calibration/__init__.py +0 -0
  91. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  92. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  93. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/calibration/calibration.py +0 -0
  94. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/__init__.py +0 -0
  95. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  96. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  97. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/daophot.py +0 -0
  98. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/pysep.py +0 -0
  99. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/simpledisk.py +0 -0
  100. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  101. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/exptime/__init__.py +0 -0
  102. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/exptime/exptime.py +0 -0
  103. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/exptime/star.py +0 -0
  104. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/__init__.py +0 -0
  105. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  106. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/download.py +0 -0
  107. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/flip.py +0 -0
  108. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/grayscale.py +0 -0
  109. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/httpserver.py +0 -0
  110. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/normalize.py +0 -0
  111. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/save.py +0 -0
  112. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/saveimage.py +0 -0
  113. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/smooth.py +0 -0
  114. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/image/softbin.py +0 -0
  115. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/__init__.py +0 -0
  116. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/addmask.py +0 -0
  117. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  118. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/circularmask.py +0 -0
  119. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/createfilename.py +0 -0
  120. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  121. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/misc/removebackground.py +0 -0
  122. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/modules/__init__.py +0 -0
  123. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  124. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/__init__.py +0 -0
  125. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/astrometry.py +0 -0
  126. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  127. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  128. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  129. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  130. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  131. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/offsets.py +0 -0
  132. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  133. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/projected.py +0 -0
  134. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  135. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/__init__.py +0 -0
  136. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  137. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  138. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  139. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  140. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/photometry.py +0 -0
  141. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/photutil.py +0 -0
  142. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/photometry/pysep.py +0 -0
  143. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/wcs/__init__.py +0 -0
  144. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  145. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IAbortable.py +0 -0
  146. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IAcquisition.py +0 -0
  147. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IAutoFocus.py +0 -0
  148. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IAutoGuiding.py +0 -0
  149. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IAutonomous.py +0 -0
  150. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IBinning.py +0 -0
  151. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ICalibrate.py +0 -0
  152. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ICamera.py +0 -0
  153. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IConfig.py +0 -0
  154. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ICooling.py +0 -0
  155. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IData.py +0 -0
  156. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IDataSequence.py +0 -0
  157. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IDome.py +0 -0
  158. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IExposure.py +0 -0
  159. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IExposureTime.py +0 -0
  160. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFilters.py +0 -0
  161. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  162. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  163. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFlatField.py +0 -0
  164. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFocusModel.py +0 -0
  165. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IFocuser.py +0 -0
  166. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IGain.py +0 -0
  167. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IImageFormat.py +0 -0
  168. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IImageType.py +0 -0
  169. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IMode.py +0 -0
  170. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IModule.py +0 -0
  171. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IMotion.py +0 -0
  172. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IMultiFiber.py +0 -0
  173. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
  174. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
  175. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingAltAz.py +0 -0
  176. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingBody.py +0 -0
  177. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
  178. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
  179. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
  180. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
  181. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingRaDec.py +0 -0
  182. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IPointingSeries.py +0 -0
  183. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IReady.py +0 -0
  184. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IRoof.py +0 -0
  185. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IRotation.py +0 -0
  186. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IRunnable.py +0 -0
  187. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IRunning.py +0 -0
  188. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IScriptRunner.py +0 -0
  189. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ISpectrograph.py +0 -0
  190. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IStartStop.py +0 -0
  191. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IStructuredConfig.py +0 -0
  192. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ISyncTarget.py +0 -0
  193. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ITelescope.py +0 -0
  194. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ITemperatures.py +0 -0
  195. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ITrackingMode.py +0 -0
  196. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/ITrackingRate.py +0 -0
  197. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IVideo.py +0 -0
  198. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IWeather.py +0 -0
  199. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/IWindow.py +0 -0
  200. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/interfaces/interface.py +0 -0
  201. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/__init__.py +0 -0
  202. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/camerasettings.py +0 -0
  203. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/fitsheader.py +0 -0
  204. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/fitsnamespace.py +0 -0
  205. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/follow.py +0 -0
  206. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/motionstatus.py +0 -0
  207. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/pipeline.py +0 -0
  208. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/waitformotion.py +0 -0
  209. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/mixins/weatheraware.py +0 -0
  210. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/__init__.py +0 -0
  211. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/__init__.py +0 -0
  212. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/adaptive.py +0 -0
  213. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/basecamera.py +0 -0
  214. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/basespectrograph.py +0 -0
  215. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/basevideo.py +0 -0
  216. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/dummyspectrograph.py +0 -0
  217. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/dummyvideo.py +0 -0
  218. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/camera/pipelinecamera.py +0 -0
  219. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/flatfield/__init__.py +0 -0
  220. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/flatfield/flatfield.py +0 -0
  221. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/flatfield/pointing.py +0 -0
  222. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/flatfield/scheduler.py +0 -0
  223. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/focus/__init__.py +0 -0
  224. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/focus/dummyautofocus.py +0 -0
  225. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/focus/focusmodel.py +0 -0
  226. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/focus/focusseries.py +0 -0
  227. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/image/__init__.py +0 -0
  228. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/image/imagewatcher.py +0 -0
  229. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/image/imagewriter.py +0 -0
  230. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/image/pipeline.py +0 -0
  231. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/image/seeing.py +0 -0
  232. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/module.py +0 -0
  233. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/__init__.py +0 -0
  234. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/_base.py +0 -0
  235. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/_baseguiding.py +0 -0
  236. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/acquisition.py +0 -0
  237. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/autoguiding.py +0 -0
  238. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  239. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/dummyguiding.py +0 -0
  240. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  241. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  242. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  243. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  244. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  245. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
  246. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/robotic/pointing.py +0 -0
  247. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/robotic/scriptrunner.py +0 -0
  248. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/roof/__init__.py +0 -0
  249. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/roof/basedome.py +0 -0
  250. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/roof/baseroof.py +0 -0
  251. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/roof/dummyroof.py +0 -0
  252. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/__init__.py +0 -0
  253. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
  254. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/basetelescope.py +0 -0
  255. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
  256. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
  257. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
  258. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/test/__init__.py +0 -0
  259. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/test/standalone.py +0 -0
  260. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/__init__.py +0 -0
  261. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/dummymode.py +0 -0
  262. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/fluentlogger.py +0 -0
  263. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/httpfilecache.py +0 -0
  264. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/kiosk.py +0 -0
  265. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/matrix.py +0 -0
  266. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/stellarium.py +0 -0
  267. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/telegram.py +0 -0
  268. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/utils/trigger.py +0 -0
  269. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/weather/__init__.py +0 -0
  270. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/weather/mockweather.py +0 -0
  271. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/weather/weather.py +0 -0
  272. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/weather/weather_api.py +0 -0
  273. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/modules/weather/weather_state.py +0 -0
  274. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/object.py +0 -0
  275. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/py.typed +0 -0
  276. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/__init__.py +0 -0
  277. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/observation.py +0 -0
  278. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/__init__.py +0 -0
  279. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/_executor.py +0 -0
  280. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  281. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  282. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  283. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  284. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  285. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  286. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  287. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  288. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  289. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  290. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  291. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  292. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  293. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  294. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  295. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  296. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  297. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/random.py +0 -0
  298. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  299. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  300. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  301. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  302. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  303. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  304. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
  305. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
  306. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  307. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  308. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  309. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  310. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/targets/target.py +0 -0
  311. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  312. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/__init__.py +0 -0
  313. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  314. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  315. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  316. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  317. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/__init__.py +0 -0
  318. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/cases.py +0 -0
  319. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/conditional.py +0 -0
  320. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/parallel.py +0 -0
  321. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/selector.py +0 -0
  322. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/control/sequential.py +0 -0
  323. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/exceptions.py +0 -0
  324. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  325. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  326. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  327. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  328. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/script.py +0 -0
  329. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  330. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
  331. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  332. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/scripts/utils/log.py +0 -0
  333. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/__init__.py +0 -0
  334. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  335. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  336. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  337. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/__init__.py +0 -0
  338. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/_portal.py +0 -0
  339. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  340. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  341. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/configdb.py +0 -0
  342. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  343. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  344. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  345. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  346. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  347. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  348. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/task.py +0 -0
  349. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  350. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  351. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/memory/__init__.py +0 -0
  352. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  353. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  354. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/observationarchive.py +0 -0
  355. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/portal/__init__.py +0 -0
  356. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/portal/observationarchive.py +0 -0
  357. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/portal/taskarchive.py +0 -0
  358. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/storage/taskarchive.py +0 -0
  359. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/task.py +0 -0
  360. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/taskrunner.py +0 -0
  361. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/__init__.py +0 -0
  362. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/archive/__init__.py +0 -0
  363. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/archive/archive.py +0 -0
  364. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/archive/local_archive.py +0 -0
  365. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
  366. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  367. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  368. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  369. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  370. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  371. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  372. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  373. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  374. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  375. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  376. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  377. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  378. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  379. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  380. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/__init__.py +0 -0
  381. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/average.py +0 -0
  382. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/cache.py +0 -0
  383. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/config.py +0 -0
  384. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/config_schema.py +0 -0
  385. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/coordinates.py +0 -0
  386. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/curvefit.py +0 -0
  387. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/enums.py +0 -0
  388. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/exceptions.py +0 -0
  389. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/fits.py +0 -0
  390. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/focusseries/__init__.py +0 -0
  391. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/focusseries/base.py +0 -0
  392. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/focusseries/photometry.py +0 -0
  393. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/focusseries/projection.py +0 -0
  394. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/grids/__init__.py +0 -0
  395. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/grids/filters.py +0 -0
  396. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/grids/grid.py +0 -0
  397. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/grids/gridnode.py +0 -0
  398. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/grids/pipeline.py +0 -0
  399. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/__init__.py +0 -0
  400. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/__init__.py +0 -0
  401. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  402. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  403. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  404. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  405. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  406. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  407. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  408. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/http.py +0 -0
  409. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/influxdb.py +0 -0
  410. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/logger.py +0 -0
  411. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/logging/__init__.py +0 -0
  412. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/logging/context.py +0 -0
  413. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/logging/resolvableerror.py +0 -0
  414. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/modulegui.py +0 -0
  415. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/offsets/__init__.py +0 -0
  416. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  417. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/offsets/applyoffsets.py +0 -0
  418. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  419. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/parallel.py +0 -0
  420. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/pipeline/__init__.py +0 -0
  421. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/pipeline/pipeline.py +0 -0
  422. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/pipeline/progress.py +0 -0
  423. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/pipeline/reduction.py +0 -0
  424. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/pipeline/reduction_base.py +0 -0
  425. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/__init__.py +0 -0
  426. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/csv.py +0 -0
  427. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/http.py +0 -0
  428. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/log.py +0 -0
  429. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/multi.py +0 -0
  430. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/publisher/publisher.py +0 -0
  431. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/serialization.py +0 -0
  432. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/shellcommand.py +0 -0
  433. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/threads/__init__.py +0 -0
  434. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/threads/lockwithabort.py +0 -0
  435. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/time.py +0 -0
  436. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/units.py +0 -0
  437. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/utils/versions.py +0 -0
  438. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/version.py +0 -0
  439. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/__init__.py +0 -0
  440. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/archivefile.py +0 -0
  441. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/bufferedfile.py +0 -0
  442. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/file.py +0 -0
  443. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/filelists/__init__.py +0 -0
  444. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/filelists/filelist.py +0 -0
  445. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/filelists/testing.py +0 -0
  446. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/httpfile.py +0 -0
  447. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/localfile.py +0 -0
  448. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/memfile.py +0 -0
  449. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/sftpfile.py +0 -0
  450. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/smbfile.py +0 -0
  451. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/sshfile.py +0 -0
  452. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/tempfile.py +0 -0
  453. {pyobs_core-2.0.2 → pyobs_core-2.1.0}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyobs-core
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -4,7 +4,6 @@ from typing import Any
4
4
 
5
5
  import slixmpp
6
6
  import slixmpp.xmlstream
7
- from slixmpp.xmlstream import StanzaBase
8
7
 
9
8
  from pyobs.comm.xmpp.xep_0009.rpc import XEP_0009
10
9
  from pyobs.comm.xmpp.xep_0009_timeout import XEP_0009_timeout
@@ -71,7 +70,6 @@ class XmppClient(slixmpp.ClientXMPP):
71
70
  self.add_event_handler("failed_auth", lambda ev: self._auth(False))
72
71
  self.add_event_handler("failed_all_auth", self.failed_all_auth)
73
72
  self.add_event_handler("stream_error", self._stream_error)
74
- self.add_filter("in", self._filter_messages)
75
73
 
76
74
  def send_presence(self, *args: Any, **kwargs: Any) -> Any:
77
75
  """Override slixmpp's send_presence to hold back every presence broadcast -- the initial
@@ -112,13 +110,6 @@ class XmppClient(slixmpp.ClientXMPP):
112
110
  """
113
111
  return self.disconnect(0.0, reason=reason)
114
112
 
115
- def _filter_messages(self, stanza: StanzaBase) -> StanzaBase | None:
116
- # if a user with same JID is already connected, we get a conflict
117
- if '<conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />' in str(stanza):
118
- self._jid_conflict = True
119
- return None
120
- return stanza
121
-
122
113
  def _stream_error(self, error: Any) -> None:
123
114
  """Called when the server sends a <stream:error/>, e.g. when this connection gets
124
115
  kicked because another session took over the same JID/resource.
@@ -56,8 +56,13 @@ def _retry_delay(attempt: int, cap: float = 30.0, base: float = 1.0) -> float:
56
56
  every module reconnecting to ejabberd simultaneously after a mass restart). Fixed-interval
57
57
  retries stay in lockstep across all of them, repeatedly hammering the server at the same
58
58
  instants; jitter decorrelates that so retries spread out over time instead.
59
+
60
+ The `min(attempt, 60)` doesn't affect real backoff behavior -- with the defaults, `cap`
61
+ already clamps the delay from attempt 5 onward. It only exists so `2 ** attempt` can't grow
62
+ past what a float can hold for retry loops with no attempt limit (#824: attempt 1024 raised
63
+ OverflowError here and killed the retrying task).
59
64
  """
60
- return random.uniform(0, min(cap, base * (2**attempt)))
65
+ return random.uniform(0, min(cap, base * (2 ** min(attempt, 60))))
61
66
 
62
67
 
63
68
  def _log_task_exception(task: asyncio.Task[Any]) -> None:
@@ -743,7 +748,8 @@ class XmppComm(Comm):
743
748
  continue
744
749
  if (ev.__name__, ev.version) not in peer_sent_events:
745
750
  continue
746
- asyncio.create_task(self._subscribe_event_with_retry(module_name, ev))
751
+ task = asyncio.create_task(self._subscribe_event_with_retry(module_name, ev))
752
+ task.add_done_callback(_log_task_exception)
747
753
 
748
754
  # send event
749
755
  self._send_event_to_module(ModuleOpenedEvent(), msg["from"].username)
@@ -904,7 +910,17 @@ class XmppComm(Comm):
904
910
  if (ev.__name__, ev.version) not in self._peer_sent_events.get(peer_jid, set()):
905
911
  continue
906
912
  peer_module = peer_jid[: peer_jid.index("@")]
907
- asyncio.create_task(self._subscribe_event_with_retry(peer_module, ev))
913
+ task = asyncio.create_task(self._subscribe_event_with_retry(peer_module, ev))
914
+ task.add_done_callback(_log_task_exception)
915
+ elif self._module is not None:
916
+ # send-only declaration -- pre-create the node now, before this module announces
917
+ # presence, so peers reacting to it in _got_online land their subscribe on the
918
+ # first attempt instead of falling back to the retry loop (see #824). Sequential
919
+ # and awaited inside open(): each _create_node can burn up to ~90s against an
920
+ # unresponsive pubsub service (_safe_send's 5 x 15s timeout budget plus jittered
921
+ # waits between attempts), times the number of send-only events this module
922
+ # declares. Accepted since connect fails anyway in that scenario.
923
+ await self._create_node(self._event_node(self._module.name, ev))
908
924
 
909
925
  # update caps and send presence
910
926
  await self._safe_send(self.client.plugin["xep_0115"].update_caps)
@@ -939,6 +955,20 @@ class XmppComm(Comm):
939
955
  return parts[2]
940
956
  return None
941
957
 
958
+ async def _create_node(self, node: str) -> None:
959
+ """Pre-create a pubsub node so a subscriber doesn't have to wait for the first publish.
960
+
961
+ The realistic error here is <conflict/> on restart (nodes persist server-side), and a
962
+ permission denial should degrade gracefully to today's lazy auto-create, never block
963
+ startup -- so IqError is swallowed. IqTimeout is already retried inside _safe_send; if it
964
+ still runs out, a dead server shouldn't hang open() any longer than that existing budget,
965
+ so it's swallowed here too rather than propagating.
966
+ """
967
+ try:
968
+ await self._safe_send(self.client.plugin["xep_0060"].create_node, self._pubsub_service, node)
969
+ except (slixmpp.exceptions.IqError, slixmpp.exceptions.IqTimeout) as e:
970
+ log.debug("Could not pre-create pubsub node %s (%s), will lazy-create on first publish.", node, e)
971
+
942
972
  async def _subscribe_event_with_retry(self, peer_module: str, event_class: type[Event]) -> None:
943
973
  """Subscribe to a peer's event node, retrying until the node exists.
944
974
 
@@ -952,20 +982,28 @@ class XmppComm(Comm):
952
982
  return
953
983
  self._event_subscriptions.add(key)
954
984
  node = self._event_node(peer_module, event_class)
955
- attempt = 0
956
- while key in self._event_subscriptions:
957
- try:
958
- await self._safe_send(self.client.plugin["xep_0060"].subscribe, self._pubsub_service, node)
959
- return
960
- except (slixmpp.exceptions.IqError, slixmpp.exceptions.IqTimeout):
961
- attempt += 1
962
- if attempt == 30:
963
- log.warning(
964
- "Still failing to subscribe to event node %s after %d attempts, will keep retrying",
965
- node,
966
- attempt,
967
- )
968
- await asyncio.sleep(_retry_delay(attempt))
985
+ try:
986
+ attempt = 0
987
+ while key in self._event_subscriptions:
988
+ try:
989
+ await self._safe_send(self.client.plugin["xep_0060"].subscribe, self._pubsub_service, node)
990
+ return
991
+ except (slixmpp.exceptions.IqError, slixmpp.exceptions.IqTimeout):
992
+ attempt += 1
993
+ if attempt == 30:
994
+ log.warning(
995
+ "Still failing to subscribe to event node %s after %d attempts, will keep retrying",
996
+ node,
997
+ attempt,
998
+ )
999
+ await asyncio.sleep(_retry_delay(attempt))
1000
+ except Exception:
1001
+ # an unexpected (non-IqError/IqTimeout) failure must not leave key permanently
1002
+ # marking (peer_module, event_class) as subscribed while nothing is subscribed and
1003
+ # nothing is retrying -- discard it so a later register_event()/_got_online()
1004
+ # re-subscribes from scratch instead of short-circuiting on the stale key (#824).
1005
+ self._event_subscriptions.discard(key)
1006
+ raise
969
1007
 
970
1008
  def _handle_event_sync(self, msg: Any) -> None:
971
1009
  """Synchronous entry point for the MatchXMLMask Callback.
@@ -1058,11 +1096,7 @@ class XmppComm(Comm):
1058
1096
  Args:
1059
1097
  method: Method to call.
1060
1098
  *args: Parameters for method.
1061
- **kwargs: Parameters for method. except slixmpp.exceptions.IqError:
1062
- raise RemoteException("Could not send command.")
1063
- except slixmpp.exceptions.IqTimeout:
1064
- raise exc()
1065
-
1099
+ **kwargs: Parameters for method.
1066
1100
 
1067
1101
  Returns:
1068
1102
  Return value from method.
@@ -1302,24 +1336,40 @@ class XmppComm(Comm):
1302
1336
  which exits the loop into the else clause (not the break path) and returns.
1303
1337
  Once subscribed, fetches the current value and dispatches it.
1304
1338
  """
1305
- attempt = 0
1306
- while node in self._state_node_handlers:
1307
- try:
1308
- await self._safe_send(self.client.plugin["xep_0060"].subscribe, self._pubsub_service, node)
1309
- break
1310
- except (slixmpp.exceptions.IqError, slixmpp.exceptions.IqTimeout):
1311
- attempt += 1
1312
- if attempt == 30:
1313
- log.warning(
1314
- "Still failing to subscribe to state node %s after %d attempts, will keep retrying",
1315
- node,
1316
- attempt,
1317
- )
1318
- await asyncio.sleep(_retry_delay(attempt))
1319
- else:
1320
- return
1321
-
1322
- # Fetch current value immediately after subscribing
1339
+ try:
1340
+ attempt = 0
1341
+ while node in self._state_node_handlers:
1342
+ try:
1343
+ await self._safe_send(self.client.plugin["xep_0060"].subscribe, self._pubsub_service, node)
1344
+ break
1345
+ except (slixmpp.exceptions.IqError, slixmpp.exceptions.IqTimeout):
1346
+ attempt += 1
1347
+ if attempt == 30:
1348
+ log.warning(
1349
+ "Still failing to subscribe to state node %s after %d attempts, will keep retrying",
1350
+ node,
1351
+ attempt,
1352
+ )
1353
+ await asyncio.sleep(_retry_delay(attempt))
1354
+ else:
1355
+ return
1356
+ except Exception:
1357
+ # an unexpected failure in the subscribe loop itself must not leave a
1358
+ # _state_node_handlers entry behind forever -- that would permanently short-circuit
1359
+ # _subscribe_state (see its "already subscribed" branch) while nothing is actually
1360
+ # subscribed and nothing is retrying (#824). A later _subscribe_state call starts a
1361
+ # fresh subscribe from scratch instead; note it only carries the callback that
1362
+ # triggered that fresh call, not any earlier ones -- accepted as strictly better than
1363
+ # permanent silent loss for this catastrophic, unexpected case. Logged by the task's
1364
+ # _log_task_exception done-callback, not here, to avoid printing the traceback twice.
1365
+ self._state_node_handlers.pop(node, None)
1366
+ raise
1367
+
1368
+ # Fetch current value immediately after subscribing. Deliberately outside the block
1369
+ # above: a bad payload (_xml_to_dataclass) or a raising callback here must not be treated
1370
+ # as a failed subscription -- the server-side subscription is live either way, so
1371
+ # discarding the handler would just recreate #824's permanent-silent-loss failure via a
1372
+ # different trigger while making it look like a subscribe failure.
1323
1373
  try:
1324
1374
  result = await self._safe_send(
1325
1375
  self.client.plugin["xep_0060"].get_items, self._pubsub_service, node, max_items=1
@@ -1349,7 +1399,8 @@ class XmppComm(Comm):
1349
1399
  # Subscribe in a background task so _get_client() returns immediately.
1350
1400
  # The retry loop handles the case where the publisher hasn't created
1351
1401
  # the node yet (e.g. GUI connects before camera's first publish).
1352
- asyncio.create_task(self._subscribe_with_retry(node, interface))
1402
+ task = asyncio.create_task(self._subscribe_with_retry(node, interface))
1403
+ task.add_done_callback(_log_task_exception)
1353
1404
 
1354
1405
  # Initial value is fetched in _subscribe_with_retry after the subscribe IQ succeeds
1355
1406
 
@@ -6,6 +6,7 @@ from pyobs.events.event import Event
6
6
  class DataType(TypedDict):
7
7
  name: str
8
8
  id: Any
9
+ obsnum: str | None
9
10
 
10
11
 
11
12
  class TaskFailedEvent(Event):
@@ -13,15 +14,16 @@ class TaskFailedEvent(Event):
13
14
 
14
15
  __module__ = "pyobs.events"
15
16
 
16
- def __init__(self, name: str, id: Any, **kwargs: Any):
17
+ def __init__(self, name: str, id: Any, obsnum: str | None = None, **kwargs: Any):
17
18
  """Initializes a new task failed event.
18
19
 
19
20
  Args:
20
- name: Name of task that just finished
21
+ name: Name of task that just failed
21
22
  id: Unique identifier for task
23
+ obsnum: Per-night observation number of the run that just failed, e.g. "20260810-001"
22
24
  """
23
25
  Event.__init__(self)
24
- self.data: DataType = {"name": name, "id": id}
26
+ self.data: DataType = {"name": name, "id": id, "obsnum": obsnum}
25
27
 
26
28
  @property
27
29
  def name(self) -> str:
@@ -31,5 +33,9 @@ class TaskFailedEvent(Event):
31
33
  def id(self) -> Any:
32
34
  return self.data["id"]
33
35
 
36
+ @property
37
+ def obsnum(self) -> str | None:
38
+ return self.data["obsnum"]
39
+
34
40
 
35
41
  __all__ = ["TaskFailedEvent"]
@@ -6,6 +6,7 @@ from pyobs.events.event import Event
6
6
  class DataType(TypedDict):
7
7
  name: str
8
8
  id: Any
9
+ obsnum: str | None
9
10
 
10
11
 
11
12
  class TaskFinishedEvent(Event):
@@ -13,15 +14,16 @@ class TaskFinishedEvent(Event):
13
14
 
14
15
  __module__ = "pyobs.events"
15
16
 
16
- def __init__(self, name: str, id: Any, **kwargs: Any):
17
+ def __init__(self, name: str, id: Any, obsnum: str | None = None, **kwargs: Any):
17
18
  """Initializes a new task finished event.
18
19
 
19
20
  Args:
20
21
  name: Name of task that just finished
21
22
  id: Unique identifier for task
23
+ obsnum: Per-night observation number of the run that just finished, e.g. "20260810-001"
22
24
  """
23
25
  Event.__init__(self)
24
- self.data: DataType = {"name": name, "id": id}
26
+ self.data: DataType = {"name": name, "id": id, "obsnum": obsnum}
25
27
 
26
28
  @property
27
29
  def name(self) -> str:
@@ -31,5 +33,9 @@ class TaskFinishedEvent(Event):
31
33
  def id(self) -> Any:
32
34
  return self.data["id"]
33
35
 
36
+ @property
37
+ def obsnum(self) -> str | None:
38
+ return self.data["obsnum"]
39
+
34
40
 
35
41
  __all__ = ["TaskFinishedEvent"]
@@ -12,6 +12,7 @@ class DataType(TypedDict):
12
12
  name: str
13
13
  id: Any
14
14
  eta: str | None
15
+ obsnum: str | None
15
16
 
16
17
 
17
18
  class TaskStartedEvent(Event):
@@ -19,16 +20,17 @@ class TaskStartedEvent(Event):
19
20
 
20
21
  __module__ = "pyobs.events"
21
22
 
22
- def __init__(self, name: str, id: Any, eta: Time | None = None, **kwargs: Any):
23
+ def __init__(self, name: str, id: Any, eta: Time | None = None, obsnum: str | None = None, **kwargs: Any):
23
24
  """Initializes a new task started event.
24
25
 
25
26
  Args:
26
27
  name: Name of task that just started
27
28
  id: Unique identifier for task
28
29
  eta: Predicted ETA for when the task will finish
30
+ obsnum: Per-night observation number assigned to this run, e.g. "20260810-001"
29
31
  """
30
32
  Event.__init__(self)
31
- self.data: DataType = {"name": name, "id": id, "eta": None if eta is None else eta.isot}
33
+ self.data: DataType = {"name": name, "id": id, "eta": None if eta is None else eta.isot, "obsnum": obsnum}
32
34
 
33
35
  @classmethod
34
36
  def from_dict(cls, d: dict[str, Any]) -> Event:
@@ -49,8 +51,11 @@ class TaskStartedEvent(Event):
49
51
  if "eta" in d and isinstance(d["eta"], str):
50
52
  eta = Time(d["eta"])
51
53
 
54
+ # get obsnum
55
+ obsnum: str | None = d["obsnum"] if isinstance(d.get("obsnum"), str) else None
56
+
52
57
  # return object
53
- return TaskStartedEvent(name=name, id=id, eta=eta)
58
+ return TaskStartedEvent(name=name, id=id, eta=eta, obsnum=obsnum)
54
59
 
55
60
  @property
56
61
  def name(self) -> str:
@@ -66,5 +71,9 @@ class TaskStartedEvent(Event):
66
71
 
67
72
  return Time(self.data["eta"]) if self.data["eta"] is not None else None
68
73
 
74
+ @property
75
+ def obsnum(self) -> str | None:
76
+ return self.data["obsnum"]
77
+
69
78
 
70
79
  __all__ = ["TaskStartedEvent"]
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABCMeta
4
+ from dataclasses import dataclass, field
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from ..utils.time import Time
8
+ from .IStartStop import IStartStop
9
+
10
+ if TYPE_CHECKING:
11
+ from ..robotic.observation import Observation
12
+
13
+
14
+ @dataclass
15
+ class RoboticTask:
16
+ id: Any
17
+ name: str
18
+ target: str | None = None # target name, resolved if available
19
+ start: Time | None = None # planned start (next) / actual start (current)
20
+ end: Time | None = None # planned end / ETA
21
+ obsnum: str | None = None # "20260810-001" once assigned by the executor
22
+ # ObservationState value (e.g. "in_progress"), kept as a plain str rather than
23
+ # pyobs.robotic.observation.ObservationState: pyobs.interfaces must not depend on
24
+ # pyobs.robotic (which itself depends on pyobs.interfaces), or module import order decides
25
+ # whether it works.
26
+ state: str | None = None
27
+ priority: float | None = None
28
+
29
+ @classmethod
30
+ def from_observation(cls, observation: Observation) -> RoboticTask:
31
+ """Build a wire-sized task summary from a full `Observation` record."""
32
+ return cls(
33
+ id=observation.task.id,
34
+ name=observation.task.name,
35
+ target=observation.target.name if observation.target is not None else None,
36
+ start=Time(observation.start),
37
+ end=Time(observation.end),
38
+ obsnum=observation.obsnum,
39
+ state=observation.state.value,
40
+ priority=observation.priority,
41
+ )
42
+
43
+
44
+ @dataclass
45
+ class RoboticState:
46
+ current: RoboticTask | None = None
47
+ next: RoboticTask | None = None # immediate next observation to run
48
+ cant_run_reason: str | None = None # from TaskRunner.cant_run_reason(), for `next`
49
+ time: Time = field(default_factory=Time.now)
50
+
51
+
52
+ class IRobotic(IStartStop, metaclass=ABCMeta):
53
+ """The module executes a schedule of tasks (e.g. Mastermind)."""
54
+
55
+ __module__ = "pyobs.interfaces"
56
+
57
+ state = RoboticState
58
+
59
+
60
+ __all__ = ["IRobotic", "RoboticTask", "RoboticState"]
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from dataclasses import dataclass, field
5
+ from typing import Any
6
+
7
+ from ..utils.time import Time
8
+ from .IRobotic import RoboticTask
9
+ from .IStartStop import IStartStop
10
+
11
+
12
+ @dataclass
13
+ class SchedulerState:
14
+ last_reschedule: Time | None = None
15
+ time: Time = field(default_factory=Time.now)
16
+
17
+
18
+ class IRoboticScheduler(IStartStop, metaclass=ABCMeta):
19
+ """The module plans a schedule of tasks for an IRobotic executor to run (e.g. Scheduler)."""
20
+
21
+ __module__ = "pyobs.interfaces"
22
+
23
+ state = SchedulerState
24
+
25
+ @abstractmethod
26
+ async def get_schedule(self, limit: int = 20, **kwargs: Any) -> list[RoboticTask]:
27
+ """Return the upcoming (pending/in-progress) schedule, most imminent first.
28
+
29
+ Args:
30
+ limit: Maximum number of entries to return. The implementation also enforces its
31
+ own hard ceiling regardless of what's requested here, so the full schedule
32
+ never has to go over the wire.
33
+
34
+ Returns:
35
+ Up to `limit` scheduled tasks.
36
+ """
37
+ ...
38
+
39
+
40
+ __all__ = ["IRoboticScheduler", "SchedulerState"]
@@ -52,6 +52,8 @@ from .IPointingOrbitalElements import IPointingOrbitalElements, OrbitalElements
52
52
  from .IPointingRaDec import IPointingRaDec, RaDecState
53
53
  from .IPointingSeries import IPointingSeries
54
54
  from .IReady import IReady, ReadyState
55
+ from .IRobotic import IRobotic, RoboticState, RoboticTask
56
+ from .IRoboticScheduler import IRoboticScheduler, SchedulerState
55
57
  from .IRoof import IRoof
56
58
  from .IRotation import IRotation, RotationState
57
59
  from .IRunnable import IRunnable
@@ -149,6 +151,11 @@ __all__ = [
149
151
  "IPointingSeries",
150
152
  "IReady",
151
153
  "ReadyState",
154
+ "IRobotic",
155
+ "RoboticTask",
156
+ "RoboticState",
157
+ "IRoboticScheduler",
158
+ "SchedulerState",
152
159
  "IRoof",
153
160
  "IRotation",
154
161
  "RotationState",
@@ -212,7 +212,9 @@ class DummyCamera(BaseCamera, IWindow, IBinning, ICooling, IGain, IImageFormat):
212
212
  & (sources["y_mean"] < shape[0])
213
213
  ]
214
214
  model = models.Moffat2D()
215
- data += make_model_image(shape, model, sources, model_shape=(15, 15))
215
+ data += make_model_image(
216
+ shape, model, sources, model_shape=(15, 15), params_map={"x_0": "x_mean", "y_0": "y_mean"}
217
+ )
216
218
 
217
219
  data[data > 65535] = 65535
218
220
  return data.astype(np.uint16)
@@ -5,9 +5,11 @@ TODO: write doc
5
5
 
6
6
  __title__ = "Robotic mode"
7
7
 
8
+ from .dummymastermind import DummyMastermind
9
+ from .dummyscheduler import DummyScheduler
8
10
  from .mastermind import Mastermind
9
11
  from .pointing import PointingSeries
10
12
  from .scheduler import Scheduler
11
13
  from .scriptrunner import ScriptRunner
12
14
 
13
- __all__ = ["Mastermind", "PointingSeries", "Scheduler", "ScriptRunner"]
15
+ __all__ = ["DummyMastermind", "DummyScheduler", "Mastermind", "PointingSeries", "Scheduler", "ScriptRunner"]