dsf-python 3.4.6__tar.gz → 3.4.6.post2__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 (177) hide show
  1. {dsf-python-3.4.6/src/dsf_python.egg-info → dsf-python-3.4.6.post2}/PKG-INFO +17 -20
  2. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/setup.py +1 -1
  3. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code.py +1 -1
  4. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/init_messages/client_init_messages.py +1 -0
  5. {dsf-python-3.4.6 → dsf-python-3.4.6.post2/src/dsf_python.egg-info}/PKG-INFO +17 -20
  6. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf_python.egg-info/SOURCES.txt +7 -1
  7. dsf-python-3.4.6.post2/tests/test_code.py +37 -0
  8. dsf-python-3.4.6.post2/tests/test_custom_http_endpoint.py +53 -0
  9. dsf-python-3.4.6.post2/tests/test_custom_m_codes.py +70 -0
  10. dsf-python-3.4.6.post2/tests/test_object_model.py +149 -0
  11. dsf-python-3.4.6.post2/tests/test_send_simple_code.py +46 -0
  12. dsf-python-3.4.6.post2/tests/test_subscribe_object_model.py +54 -0
  13. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/LICENSE +0 -0
  14. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/MANIFEST.in +0 -0
  15. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/README.md +0 -0
  16. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/pyproject.toml +0 -0
  17. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/setup.cfg +0 -0
  18. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/__init__.py +0 -0
  19. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/__init__.py +0 -0
  20. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/base_command.py +0 -0
  21. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code_channel.py +0 -0
  22. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code_flags.py +0 -0
  23. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code_interception.py +0 -0
  24. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code_parameter.py +0 -0
  25. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/code_type.py +0 -0
  26. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/condition_type.py +0 -0
  27. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/files.py +0 -0
  28. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/generic.py +0 -0
  29. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/http_endpoints.py +0 -0
  30. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/model_subscription.py +0 -0
  31. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/object_model.py +0 -0
  32. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/packages.py +0 -0
  33. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/plugins.py +0 -0
  34. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/responses.py +0 -0
  35. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/commands/user_sessions.py +0 -0
  36. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/__init__.py +0 -0
  37. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/base_command_connection.py +0 -0
  38. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/base_connection.py +0 -0
  39. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/command_connection.py +0 -0
  40. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/exceptions.py +0 -0
  41. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/init_messages/__init__.py +0 -0
  42. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/init_messages/server_init_message.py +0 -0
  43. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/intercept_connection.py +0 -0
  44. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/connections/subscribe_connection.py +0 -0
  45. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/exceptions.py +0 -0
  46. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/http.py +0 -0
  47. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/__init__.py +0 -0
  48. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/__init__.py +0 -0
  49. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/accelerometer.py +0 -0
  50. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/boards.py +0 -0
  51. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/closed_loop.py +0 -0
  52. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/direct_display.py +0 -0
  53. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/driver.py +0 -0
  54. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/driver_settings.py +0 -0
  55. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/min_max_current.py +0 -0
  56. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/boards/stall_detect_settings.py +0 -0
  57. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/directories/__init__.py +0 -0
  58. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/directories/directories.py +0 -0
  59. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/fans/__init__.py +0 -0
  60. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/fans/fan_thermostatic_control.py +0 -0
  61. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/fans/fans.py +0 -0
  62. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/__init__.py +0 -0
  63. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/heat.py +0 -0
  64. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/heater.py +0 -0
  65. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/heater_model.py +0 -0
  66. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/heater_model_pid.py +0 -0
  67. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/heat/heater_monitor.py +0 -0
  68. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/http_endpoints/__init__.py +0 -0
  69. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/http_endpoints/http_endpoint_type.py +0 -0
  70. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/http_endpoints/http_endpoints.py +0 -0
  71. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/inputs/__init__.py +0 -0
  72. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/inputs/compatibility.py +0 -0
  73. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/inputs/distance_unit.py +0 -0
  74. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/inputs/input_channel.py +0 -0
  75. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/inputs/input_channel_state.py +0 -0
  76. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/__init__.py +0 -0
  77. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/build.py +0 -0
  78. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/build_object.py +0 -0
  79. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/gcode_fileinfo.py +0 -0
  80. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/job.py +0 -0
  81. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/layer.py +0 -0
  82. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/thumbnail_info.py +0 -0
  83. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/job/times_left.py +0 -0
  84. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/limits/__init__.py +0 -0
  85. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/limits/limits.py +0 -0
  86. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/messages/__init__.py +0 -0
  87. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/messages/messages.py +0 -0
  88. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/model_collection.py +0 -0
  89. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/model_dictionary.py +0 -0
  90. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/model_object.py +0 -0
  91. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/__init__.py +0 -0
  92. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/axis.py +0 -0
  93. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/current_move.py +0 -0
  94. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/driver_id.py +0 -0
  95. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/extruder.py +0 -0
  96. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/extruder_non_linear.py +0 -0
  97. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/input_shaping.py +0 -0
  98. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/__init__.py +0 -0
  99. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/core_kinematics.py +0 -0
  100. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/delta_kinematics.py +0 -0
  101. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/delta_tower.py +0 -0
  102. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/hangprinter_kinematics.py +0 -0
  103. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/kinematics.py +0 -0
  104. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/kinematics_name.py +0 -0
  105. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/polar_kinematics.py +0 -0
  106. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/scara_kinematics.py +0 -0
  107. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/tilt_correction.py +0 -0
  108. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/kinematics/zleadscrew_kinematics.py +0 -0
  109. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/microstepping.py +0 -0
  110. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/motors_idle_control.py +0 -0
  111. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move.py +0 -0
  112. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_calibration.py +0 -0
  113. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_compensation.py +0 -0
  114. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_deviations.py +0 -0
  115. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_queue_item.py +0 -0
  116. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_rotation.py +0 -0
  117. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/move_segmentation.py +0 -0
  118. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/probe_grid.py +0 -0
  119. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/move/skew.py +0 -0
  120. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/__init__.py +0 -0
  121. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/network.py +0 -0
  122. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/network_interface.py +0 -0
  123. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/network_interface_type.py +0 -0
  124. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/network_protocol.py +0 -0
  125. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/network/network_state.py +0 -0
  126. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/object_model.py +0 -0
  127. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/plugins/__init__.py +0 -0
  128. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/plugins/plugin_manifest.py +0 -0
  129. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/plugins/plugins.py +0 -0
  130. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/plugins/sbc_permissions.py +0 -0
  131. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/scanner/__init__.py +0 -0
  132. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/scanner/scanner.py +0 -0
  133. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/scanner/scanner_status.py +0 -0
  134. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/__init__.py +0 -0
  135. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/analog_sensor.py +0 -0
  136. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/analog_sensor_type.py +0 -0
  137. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/endstop.py +0 -0
  138. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/endstop_type.py +0 -0
  139. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/__init__.py +0 -0
  140. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/filament_monitor.py +0 -0
  141. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/filament_monitor_status.py +0 -0
  142. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/filament_monitor_type.py +0 -0
  143. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/laser_filament_monitor.py +0 -0
  144. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/pulsed_filament_monitor.py +0 -0
  145. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/filament_monitors/rotating_magnet_filament_monitor.py +0 -0
  146. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/gp_input_port.py +0 -0
  147. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/probe.py +0 -0
  148. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/probe_type.py +0 -0
  149. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/sensors/sensors.py +0 -0
  150. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/spindles/__init__.py +0 -0
  151. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/spindles/spindle_state.py +0 -0
  152. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/spindles/spindles.py +0 -0
  153. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/__init__.py +0 -0
  154. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/beep_request.py +0 -0
  155. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/gp_output_port.py +0 -0
  156. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/log_level.py +0 -0
  157. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/machine_mode.py +0 -0
  158. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/machine_status.py +0 -0
  159. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/message_box.py +0 -0
  160. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/restore_point.py +0 -0
  161. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/state/state.py +0 -0
  162. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/tools/__init__.py +0 -0
  163. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/tools/tool_retraction.py +0 -0
  164. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/tools/tool_state.py +0 -0
  165. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/tools/tools.py +0 -0
  166. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/user_sessions/__init__.py +0 -0
  167. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/user_sessions/access_level.py +0 -0
  168. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/user_sessions/session_type.py +0 -0
  169. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/user_sessions/user_sessions.py +0 -0
  170. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/utils.py +0 -0
  171. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/volumes/__init__.py +0 -0
  172. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/object_model/volumes/volumes.py +0 -0
  173. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/utility/__init__.py +0 -0
  174. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf/utils.py +0 -0
  175. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf_python.egg-info/dependency_links.txt +0 -0
  176. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf_python.egg-info/requires.txt +0 -0
  177. {dsf-python-3.4.6 → dsf-python-3.4.6.post2}/src/dsf_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsf-python
3
- Version: 3.4.6
3
+ Version: 3.4.6.post2
4
4
  Summary: Python interface to access DuetSoftwareFramework
5
5
  Home-page: https://github.com/Duet3D/dsf-python
6
6
  Author: Duet3D Ltd.
@@ -9,6 +9,22 @@ License: UNKNOWN
9
9
  Project-URL: Duet3D Support, https://forum.duet3d.com/
10
10
  Project-URL: Bug Reports, https://github.com/Duet3D/dsf-python/issues
11
11
  Project-URL: Source, https://github.com/Duet3D/dsf-python/
12
+ Description: # Duet Software Framework Python Bindings
13
+
14
+ This is also availabe as a [pip package on pypi](https://pypi.org/project/dsf-python/)
15
+
16
+ Find out more about [Duet Software Framework](https://github.com/Duet3D/DuetSoftwareFramework).
17
+
18
+ Find out more about [Duet Software Framework Python Bindings](https://github.com/Duet3D/dsf-python).
19
+
20
+ Get in touch with the community at [Duet Software Framework Forum](https://forum.duet3d.com/category/31/dsf-development) for bug reports, discussion and any kind of exchange.
21
+
22
+ ## Installation
23
+ This package contains a `setup.py` so it can be installed with `python3 setup.py install`.
24
+
25
+ ## Usage
26
+ See included `examples/` folder for various use cases.
27
+
12
28
  Keywords: Duet3D,DuetSoftwareFramework,DSF,dsf-python
13
29
  Platform: UNKNOWN
14
30
  Classifier: Development Status :: 5 - Production/Stable
@@ -23,22 +39,3 @@ Classifier: Programming Language :: Python :: 3 :: Only
23
39
  Requires-Python: >=3.7, <4
24
40
  Description-Content-Type: text/markdown
25
41
  Provides-Extra: dev
26
- License-File: LICENSE
27
-
28
- # Duet Software Framework Python Bindings
29
-
30
- This is also availabe as a [pip package on pypi](https://pypi.org/project/dsf-python/)
31
-
32
- Find out more about [Duet Software Framework](https://github.com/Duet3D/DuetSoftwareFramework).
33
-
34
- Find out more about [Duet Software Framework Python Bindings](https://github.com/Duet3D/dsf-python).
35
-
36
- Get in touch with the community at [Duet Software Framework Forum](https://forum.duet3d.com/category/31/dsf-development) for bug reports, discussion and any kind of exchange.
37
-
38
- ## Installation
39
- This package contains a `setup.py` so it can be installed with `python3 setup.py install`.
40
-
41
- ## Usage
42
- See included `examples/` folder for various use cases.
43
-
44
-
@@ -6,7 +6,7 @@ long_description = open("README.md", encoding="utf-8").read()
6
6
 
7
7
  setuptools.setup(
8
8
  name="dsf-python",
9
- version="3.4.6",
9
+ version="3.4.6-2",
10
10
  description="Python interface to access DuetSoftwareFramework",
11
11
  long_description=long_description,
12
12
  long_description_content_type="text/markdown",
@@ -79,7 +79,7 @@ class Code(BaseCommand):
79
79
  @property
80
80
  def is_from_file_channel(self) -> bool:
81
81
  """Check if this code is from a file channel"""
82
- return self.channel is CodeChannel.File or self.channel is CodeChannel.File2
82
+ return self.channel is CodeChannel.File
83
83
 
84
84
  def parameter(self, letter: str, default=None):
85
85
  """Retrieve the parameter whose letter equals c or generate a default parameter"""
@@ -17,6 +17,7 @@ clientinitmessages holds all messages a client can send to the server to initiat
17
17
  You should have received a copy of the GNU Lesser General Public License
18
18
  along with this program. If not, see <https://www.gnu.org/licenses/>.
19
19
  """
20
+ from __future__ import annotations
20
21
  from .server_init_message import ServerInitMessage
21
22
  from .. import ConnectionMode, InterceptionMode, SubscriptionMode
22
23
  from ...commands.code_channel import CodeChannel
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsf-python
3
- Version: 3.4.6
3
+ Version: 3.4.6.post2
4
4
  Summary: Python interface to access DuetSoftwareFramework
5
5
  Home-page: https://github.com/Duet3D/dsf-python
6
6
  Author: Duet3D Ltd.
@@ -9,6 +9,22 @@ License: UNKNOWN
9
9
  Project-URL: Duet3D Support, https://forum.duet3d.com/
10
10
  Project-URL: Bug Reports, https://github.com/Duet3D/dsf-python/issues
11
11
  Project-URL: Source, https://github.com/Duet3D/dsf-python/
12
+ Description: # Duet Software Framework Python Bindings
13
+
14
+ This is also availabe as a [pip package on pypi](https://pypi.org/project/dsf-python/)
15
+
16
+ Find out more about [Duet Software Framework](https://github.com/Duet3D/DuetSoftwareFramework).
17
+
18
+ Find out more about [Duet Software Framework Python Bindings](https://github.com/Duet3D/dsf-python).
19
+
20
+ Get in touch with the community at [Duet Software Framework Forum](https://forum.duet3d.com/category/31/dsf-development) for bug reports, discussion and any kind of exchange.
21
+
22
+ ## Installation
23
+ This package contains a `setup.py` so it can be installed with `python3 setup.py install`.
24
+
25
+ ## Usage
26
+ See included `examples/` folder for various use cases.
27
+
12
28
  Keywords: Duet3D,DuetSoftwareFramework,DSF,dsf-python
13
29
  Platform: UNKNOWN
14
30
  Classifier: Development Status :: 5 - Production/Stable
@@ -23,22 +39,3 @@ Classifier: Programming Language :: Python :: 3 :: Only
23
39
  Requires-Python: >=3.7, <4
24
40
  Description-Content-Type: text/markdown
25
41
  Provides-Extra: dev
26
- License-File: LICENSE
27
-
28
- # Duet Software Framework Python Bindings
29
-
30
- This is also availabe as a [pip package on pypi](https://pypi.org/project/dsf-python/)
31
-
32
- Find out more about [Duet Software Framework](https://github.com/Duet3D/DuetSoftwareFramework).
33
-
34
- Find out more about [Duet Software Framework Python Bindings](https://github.com/Duet3D/dsf-python).
35
-
36
- Get in touch with the community at [Duet Software Framework Forum](https://forum.duet3d.com/category/31/dsf-development) for bug reports, discussion and any kind of exchange.
37
-
38
- ## Installation
39
- This package contains a `setup.py` so it can be installed with `python3 setup.py install`.
40
-
41
- ## Usage
42
- See included `examples/` folder for various use cases.
43
-
44
-
@@ -167,4 +167,10 @@ src/dsf_python.egg-info/PKG-INFO
167
167
  src/dsf_python.egg-info/SOURCES.txt
168
168
  src/dsf_python.egg-info/dependency_links.txt
169
169
  src/dsf_python.egg-info/requires.txt
170
- src/dsf_python.egg-info/top_level.txt
170
+ src/dsf_python.egg-info/top_level.txt
171
+ tests/test_code.py
172
+ tests/test_custom_http_endpoint.py
173
+ tests/test_custom_m_codes.py
174
+ tests/test_object_model.py
175
+ tests/test_send_simple_code.py
176
+ tests/test_subscribe_object_model.py
@@ -0,0 +1,37 @@
1
+ import unittest
2
+ import json
3
+
4
+ from src.dsf.commands.code import Code, CodeChannel, CodeFlags, CodeType, KeywordType
5
+
6
+
7
+ class Model(unittest.TestCase):
8
+
9
+ def setUp(self):
10
+ self.maxDiff = None
11
+
12
+ def tearDown(self):
13
+ pass
14
+
15
+ def test_code(self):
16
+ json_str = '{"sourceConnection":30,"result":null,"type":"M","channel":"HTTP","lineNumber":null,"indent":0,"keyword":0,"keywordArgument":null,"majorNumber":98,"minorNumber":null,"flags":2048,"comment":null,"filePosition":null,"length":22,"parameters":[{"letter":"P","value":"0:/macros/test","isString":true}],"command":"Code"}'
17
+ c = Code.from_json(json.loads(json_str))
18
+ self.assertEqual(str(c), 'M98 P"0:/macros/test"')
19
+ self.assertEqual(c.type, CodeType.MCode)
20
+ self.assertEqual(c.majorNumber, 98)
21
+ self.assertEqual(c.minorNumber, None)
22
+ self.assertEqual(c.flags, CodeFlags.IsLastCode)
23
+ self.assertEqual(c.keyword, KeywordType.KeywordNone)
24
+ self.assertEqual(c.keywordArgument, None)
25
+ self.assertEqual(c.comment, None)
26
+ self.assertEqual(c.length, 22)
27
+ self.assertEqual(c.lineNumber, None)
28
+ self.assertEqual(c.filePosition, None)
29
+ self.assertEqual(c.indent, 0)
30
+ self.assertEqual(c.channel, CodeChannel.HTTP)
31
+ self.assertEqual(c.command, 'Code')
32
+
33
+ def test_code_keyword(self):
34
+ json_str = '{"sourceConnection":33,"result":null,"type":"K","channel":"HTTP","lineNumber":null,"indent":0,"keyword":9,"keywordArgument":"test","majorNumber":null,"minorNumber":null,"flags":2048,"comment":null,"filePosition":null,"length":12,"parameters":[],"command":"Code"}'
35
+ c = Code.from_json(json.loads(json_str))
36
+ self.assertEqual(c.type, CodeType.Keyword)
37
+ self.assertEqual(c.keyword, KeywordType.Echo)
@@ -0,0 +1,53 @@
1
+ import threading
2
+ import os
3
+ import pathlib
4
+ import socket
5
+ import time
6
+ import importlib.util
7
+
8
+ here = pathlib.Path(__file__).parent.parent.resolve()
9
+ example_path = here / "examples/custom_http_endpoint.py"
10
+ spec = importlib.util.spec_from_file_location("custom_http_endpoint", example_path)
11
+ custom_http_endpoint = importlib.util.module_from_spec(spec)
12
+ spec.loader.exec_module(custom_http_endpoint)
13
+
14
+
15
+ def test_custom_http_endpoint(monkeypatch, tmp_path):
16
+ mock_dcs_socket_file = os.path.join(tmp_path, "dsf.socket")
17
+ monkeypatch.setattr(
18
+ "dsf.connections.CommandConnection.connect.__defaults__",
19
+ (mock_dcs_socket_file,),
20
+ )
21
+
22
+ dcs_passed = threading.Event()
23
+
24
+ def mock_dcs():
25
+ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
26
+ server.bind(mock_dcs_socket_file)
27
+ server.listen(1)
28
+ conn, _ = server.accept()
29
+ conn.sendall('{"version":11, "id":"foobar"}'.encode())
30
+ assert conn.recv(1024) == b'{"mode":"Command","version":11}'
31
+ conn.sendall('{"success":true}'.encode())
32
+ assert (
33
+ conn.recv(1024) == b"{"
34
+ b'"command":"AddHttpEndpoint","EndpointType":"GET",'
35
+ b'"Namespace":"custom","Path":"getIt","IsUploadRequest":false'
36
+ b"}"
37
+ )
38
+ conn.sendall('{"result":"/var/run/dsf/custom/getIt-GET.sock","success":true}'.encode())
39
+ conn.close()
40
+ dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down
41
+
42
+ thread = threading.Thread(target=mock_dcs, daemon=True)
43
+ thread.start()
44
+ time.sleep(1)
45
+
46
+ cmd_conn, endpoint = custom_http_endpoint.custom_http_endpoint()
47
+ dcs_passed.wait(5)
48
+ endpoint.close()
49
+ cmd_conn.close()
50
+
51
+ thread.join()
52
+
53
+ assert dcs_passed.is_set()
@@ -0,0 +1,70 @@
1
+ import threading
2
+ import os
3
+ import pathlib
4
+ import socket
5
+ import time
6
+ import importlib.util
7
+
8
+ here = pathlib.Path(__file__).parent.parent.resolve()
9
+ example_path = here / "examples/custom_m_codes.py"
10
+ spec = importlib.util.spec_from_file_location("custom_m_codes", example_path)
11
+ custom_m_codes = importlib.util.module_from_spec(spec)
12
+ spec.loader.exec_module(custom_m_codes)
13
+
14
+
15
+ def test_custom_m_codes(monkeypatch, tmp_path):
16
+ mock_dcs_socket_file = os.path.join(tmp_path, "dsf.socket")
17
+ monkeypatch.setattr(
18
+ "dsf.connections.InterceptConnection.connect.__defaults__",
19
+ (mock_dcs_socket_file,),
20
+ )
21
+
22
+ dcs_passed = threading.Event()
23
+
24
+ def mock_dcs():
25
+ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
26
+ server.bind(mock_dcs_socket_file)
27
+ server.listen(1)
28
+ conn, _ = server.accept()
29
+ print("send all")
30
+ conn.sendall(b'{"version":11, "id":"foobar"}')
31
+ assert (
32
+ conn.recv(1024) == b"{"
33
+ b'"mode":"Intercept","version":11,"InterceptionMode":"Pre","Channels":["HTTP","Telnet",'
34
+ b'"File","USB","Aux","Trigger","Queue","LCD","SBC","Daemon","Aux2","AutoPause","Unknown"],'
35
+ b'"Filters":null,"PriorityCodes":false'
36
+ b"}"
37
+ )
38
+ conn.sendall(b'{"success":true}')
39
+ conn.sendall(
40
+ b"{"
41
+ b'"connection":{"id":12,"apiVersion":10,"isConnected":true},"sourceConnection":12,'
42
+ b'"result":null,"type":"M","channel":"HTTP","lineNumber":null,"indent":0,"keyword":0,'
43
+ b'"keywordArgument":null,"majorNumber":1234,"minorNumber":null,"flags":2048,"comment":null,'
44
+ b'"filePosition":null,"length":6,"parameters":[],"command":"Code"'
45
+ b"}"
46
+ )
47
+ assert conn.recv(1024) == b'{"command":"Flush","Channel":"HTTP"}'
48
+ conn.sendall(b'{"result":true,"success":true}')
49
+ assert conn.recv(1024) == b'{"command":"Resolve","Type":0,"Content":null}'
50
+ conn.sendall(
51
+ b"{"
52
+ b'"connection":{"id":12,"apiVersion":10,"isConnected":true},"sourceConnection":12,'
53
+ b'"result":null,"type":"M","channel":"HTTP","lineNumber":null,"indent":0,"keyword":0,'
54
+ b'"keywordArgument":null,"majorNumber":5678,"minorNumber":null,"flags":2048,"comment":null,'
55
+ b'"filePosition":null,"length":6,"parameters":[],"command":"Code"'
56
+ b"}"
57
+ )
58
+ assert conn.recv(1024) == b'{"command":"Resolve","Type":0,"Content":null}'
59
+ conn.close()
60
+ dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down
61
+
62
+ thread = threading.Thread(target=mock_dcs, daemon=True)
63
+ thread.start()
64
+ time.sleep(1)
65
+
66
+ custom_m_codes.start_intercept()
67
+
68
+ thread.join()
69
+
70
+ assert dcs_passed.is_set()
@@ -0,0 +1,149 @@
1
+ import json
2
+ import unittest
3
+
4
+ from src.dsf.object_model import *
5
+
6
+
7
+ class Model(unittest.TestCase):
8
+
9
+ def setUp(self):
10
+ self.maxDiff = None
11
+
12
+ def tearDown(self):
13
+ pass
14
+
15
+ def test_boards(self):
16
+ model = ObjectModel()
17
+
18
+ json_patch = '{"boards": [{"accelerometer": null, "bootloaderFileName": null, "canAddress": 0, "closedLoop": null, "directDisplay": null, "firmwareDate": "2022-11-30", "firmwareFileName": "Duet3Firmware_Mini5plus.uf2", "firmwareName": "RepRapFirmware for Duet 3 Mini 5+", "firmwareVersion": "3.4.5", "iapFileNameSBC": "Duet3_SBCiap32_Mini5plus.bin", "iapFileNameSD": "Duet3_SDiap32_Mini5plus.bin", "maxHeaters": 32, "maxMotors": 7, "mcuTemp": {"current": 33.1, "max": 33.6, "min": 19.4}, "name": "Duet 3 Mini 5+", "shortName": "Mini5plus", "state": "unknown", "supports12864": true, "supportsDirectDisplay": true, "uniqueId": "F8AU7-6P6KL-K65J0-409N2-KKW1Z-Z5W3W", "v12": null, "vIn": {"current": 19.4, "max": 19.4, "min": 19.3}}]}'
19
+ model.update_from_json(json_patch)
20
+
21
+ # Voltage change
22
+ json_patch = '{"boards":[{"vIn":{"current":42.5}}]}'
23
+ model.update_from_json(json_patch)
24
+ # Check if the value has been modified
25
+ self.assertEqual(model.boards[0].v_in.current, 42.5)
26
+ # Check if other values has not been altered
27
+ self.assertEqual(model.boards[0].v_in.min, 19.3)
28
+ self.assertEqual(model.boards[0].v_in.max, 19.4)
29
+
30
+ def test_http_endpoints(self):
31
+ from src.dsf.object_model.http_endpoints.http_endpoint_type import HttpEndpointType
32
+ model = ObjectModel()
33
+
34
+ json_patch = '{"httpEndpoints":[{"endpointType":"GET","namespace":"ExecOnMcode","path":"getCmdList","isUploadRequest":false,"unixSocket":"/run/dsf/ExecOnMcode/getCmdList-GET.sock"}]}'
35
+ model.update_from_json(json_patch)
36
+ self.assertEqual(len(model.http_endpoints), 1)
37
+ self.assertEqual(model.http_endpoints[0].endpoint_type, HttpEndpointType.GET)
38
+
39
+ json_patch = '{"httpEndpoints":[{},{"endpointType":"POST","namespace":"ExecOnMcode","path":"saveCmdList","isUploadRequest":false,"unixSocket":"/run/dsf/ExecOnMcode/saveCmdList-POST.sock"}]}'
40
+ model.update_from_json(json_patch)
41
+ self.assertEqual(len(model.http_endpoints), 2)
42
+ self.assertEqual(model.http_endpoints[1].endpoint_type, HttpEndpointType.POST)
43
+
44
+ def test_job(self):
45
+ model = ObjectModel()
46
+
47
+ json_patch = '{"job":{"file":{"filament":[496.4],"fileName":"0:/gcodes/Veil_Token.gcode","generatedBy":"ideaMaker 4.2.1.5321, 2022-10-01 17:45:38 UTC\u002B0200","height":1.04,"lastModified":"2022-10-01T16:45:39+01:00","layerHeight":0.12,"numLayers":9,"printTime":798,"size":594195,"thumbnails":[{"data":"iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAUnklEQVR4nO3cfYwcZ30H8O/zzMy\u002B3d77nl\u002BuztmOA7bjGIOdBBJioDSEJkBLStOWCERTaKS2alWkqqioqpD6BxSVSlUFaktVggRUjdomBVIaikrA4Y9AYkhsY5vEdnz2\u002Bezz3u3e3b7OzPP8\u002BsfM7O5dfMmdb\u002B9s1O9HWu3u3e7s8/xmnmeefZ7fLEBERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERET0/5261gXoljuA7N5cbuADw8PDU8bc6Isa3eJ5W3MaY/NWxiyQ6Xy9o1D0gCPnguBwv8jzvzY5\u002BTMAZq3L\u002BdG\u002BvqFfHRjYXDVmm0Dv2p72bimFZswCfZ2vc4GL/a5TnPTDM0UTHBtynBe/cv78\u002BOPALABZyzI\u002BAKTev2XL1poxb96bzR4oGbPLCAqdr3GgZvIaxZrF\u002BIwJpozIsY1aTz5Vr8/86/R08SWguZZlXC8/bw1E/3ImM3ZTOl24v39ot6OlcNmYnSOOuzWj9Zhv7UBW62FHqfRKNhpYWw1FXihZ84ILPPfVmZmnD1UqZ14GGqsp7Ad7e3ftdDOFd/f37r0Qhrs3uu4tWmGbo9RmT6nMa2\u002Bho4wijcDKBJRMzBjzk4xS4yfqwZGvz5dOfKdeH19NObcBmXv7\u002B/f81tDQnlIY3j7ierc5Su1MKdW/ku0YEb8pMuMpVZwJzZmGmHP9jvPSdBiePV71L/1naer0c8AlAHY15V1P13MDcf9\u002B4\u002BgDe3PpA3PW7negtg05zphWylmPDw9FmpNheOT5pvmdv7h0/shy3vO5jRt/6U2Z7IMN4NYRx9ntKOWtdTkBwIgEZWtOOMDh5xuNRz5\u002B8eJTy3nfp0dGbtqTyf3TJs99i7vCTuVqWREzY8y4hfxUizo6EQTHHpqc\u002BBcA4Xp8/kq517oAS9kDFLbkMl/2Fby840IjGv9YCBwAapVtOxRBAKAR39cB1CBoCDAnghIkXYXcOuXhLQCW1UC2ZHMPV5X6jawCqgBcicqqASgV3etVlltE0ABQhyAQYD6qg1cS7J2B7J1PpXIAnlrOtvqy2V9sOM7bp0TgQpCOy\u002BcAXS\u002B3FUETQAA4Ruvt88D2WbHvuZzyghHgfy4DF1f1AWvkum0gBghqgPgAVDzkFgAi0c1CoBGdq6ObwALQEj03AHwAAQQmfh5K1BCa8d8CAUIIfCzdffnG9Cy3zGVryspxkZQ5OaQ6yy1xXRTaDd7Gz61Erw0A\u002BB3lDgD4EjWKMC53gKihLB6rNMKwvNzyVq3NDTiCGhS0ADXVjvXiciOJL64Q87jsFlHM/bicSYxriMrrx/FuoP1lTwA0oipel67bBnICmC4a89NhR7/RU\u002B3zhQPAU4AjUeG1UlFPF/d8RgFGJD4jALMiKCNuICra2dHWXv17btPaSSvybDkMv7ncMp8Kgie3KvX\u002BYa03uAAcpRZ8SudjF0AWQEopaChoCBwV1TIQgYFCFdFZbTbufTu3caX\u002BvGnMuZLvf3e55X252XxCtL4rB/XOfkcPZSQq81LnCgFajdbGdeiBQkZF\u002B8WN94UPgZXoW3pNAVURVACUICjLwjiE1k7NA9PLLfN6u\u002B6\u002BgxwAvAO5XOGegYH\u002BqtXbJ8Tcf3M285sbtO7zgNZBtFwighqAM2LxsrG1sshpUWjOh\u002BHlukgttPYylJpztJ4GcPbM/Pz4nO9PHWs2X7rKKqjtmczYrb29u/dlMjdWwvCmPq1vvzGVunVIqXQvFFIA1FXUowzBRSu4LNJ80fefvWzN0XwqdWSiWj014ftnj1YqJ3CVM1w3AKP7h4dvvjGdLvRovdUDsr6VMc/RhR7AS2t3REFcLbJjm\u002BPkB5RCdoV1AKLGXxZBSezseL35RI\u002BSxzamUse/MzNz\u002BUeVSum56\u002Bxsci0biP5gPr97fyY/ckdv9pZJP9zd7zqb\u002Bx29o2mlkFaq39N62cOb5QhFalVrjzTF/qDkm6c/O3n\u002B6R8Dl7u1/T1A6s6BgZvv7\u002B3fXxO7f4Pr7nCV2hHPWuW79TkA0BSpiMhkzdpTc8YeySh14ofV\u002Bol/n5k6fgQodetzHsrnR27v7d0/6qbuGXKdNztK7ct0uS6\u002BSCMUuewoTJVDe6Eu5uQG1z17rF4/81i5fOxMozF9Mvq6te7WvIE8DHj3jI7uKAP7tzjeLqtx0IXaup4zUktJZn\u002BKYfiZD54//7WVvv/RTZtGqlq/64ZU6h4ruKPguq9fi3KuVDEMT3gK3zvj\u002B9/7WbX635\u002BZnV1xg3l0dOxdQ57\u002BqyHXfdNalHElRMRMxzNfrqijE37wbI\u002BWo1\u002B\u002BcGH8G9FIes2sdQNRj90w9s\u002BjrvtAt88G3VASi6IITlv52Z\u002BdfXnnSt//lRtu\u002BPwOL/X76asYaqyHkrX2ULPx\u002BU9duPBHK33vf2y/8eSgUq/PAfCuw/oF1laPBsG3P3r\u002B3Aewhguna/4lfUphq6fQkxULr2MKUcU3Hb9OYeXj8oQVac2sJF8gQ8QzVALMQ1AXoBLfz8Tj\u002BdYMkFxdfC8DPSkRpOIp0lRcNzeup1rqdpX1FJFW/Vozc4jG9fMQ1AQoQ6L6Rd\u002B99LzIihb7EnPxdLKLaLo6paLHHgBn8VRw/J5u7c/F9UxmxxoiKMV1nRDpOWvtwFV9wAqsdQMRALUmgM7p2k4qntWIpjmjg9aJ3yhoTylK6z4Kni/tqcQQ0RRugKhRROsb0XRuGP9/LZZujbVVdGy/vey\u002BqJ6CBfNmItFkb3IwJfVLpj7b94IgXqdpN4aongGima1mXM8A3c2T6ayBRTQjFe3Dxf9d\u002BKYF84PS7oSS6WHE5Wzvz/Z0cT1\u002BfYB2Pf14Gr7WXkdpl0ut7fAKWIcziAAXjAhcqFZXI0vcTMe7kgCGglZDMPF/Og96A8BIcgBJ6\u002BzRebAsdX4wIjURGZ8Ngr\u002B5mrqVQ3my37W/3qvVBhfRYlpnp7m4fp0HSNLYk78paZfZR3JQLjwzBvFaQ4D2gfRqDd\u002BKzDZEnrqauh2u1z51UyrzibxWO3NaZVyJG/SiaWDpuL9SfTsbQVK/UBY3wPY\u002B9RGt\u002BST1TOp9pX3oiExi6d3bFWs\u002BuPxQobD59ZnM72aBd/c6zu6s1oMpAJl4ujM6dSt4aJ\u002Buk1tnkGvxesa0CBrxAZIsogFR0P24B/I7/t9asLK20rD2rIgUNXBo1pgTPY5z\u002BKnJyfFL0cL3Vflwf//2kVzu7hHPO\u002BiJ7MxovTOndX8a7d4nOVskS4QeoszJlFJIoz1USSR1DhD1qqV4LaQs7UXCEO0GlBxcgbWzDWvHHeCFWWO\u002BB2O\u002B//Vi8eTV1u2tQO8bN20aE\u002BC2Ta672wH2aqV29yldSDs676E9rExGAUl9BQpGBKJUqzNQAHoB5KGQUapV784GZeI61xEN8yoQXBDBXDxi8K2drxlzGMCTl\u002Br1r/1vuXz2auu3HGvaQA4A3tuGhzfcmU5vnbdq37Z06kBTyX0j2tmsV/F9Y0IEzwT\u002BibKVFw2AmjWXrUgQaF33gGLW8y7XwnDuzPz8eN3a2ulqdXwOmOlu7ZakCsCmsZ6ekd3Z7NimVHZsg6vu3KjUfdscd7AHK1/LSRgRXBLBuNj5s2H4jUlrf1D0/fGLtdrkbK127hQw1d2qLK0PGBpNpws78vmxbel0YcB1\u002B\u002BrGjAiQs9YOp5TyUsoZCWGRh9ryhpS3b1RpvZq6F62dTAv\u002BayoMn3PEPH\u002B60Rh/dGbm0lqunXS1gbwZ6Hvn0NCet\u002Bfzt9dE7dvkOfuMYDSr1Ei3p3RDkYuTgf/Xf3Du3BfORxkk14178/mRB3sHDuRd9Z4R1zvoKbWj2\u002BsgRqTZsPbEnLGHG7CP/\u002BW5c9//CbDsNJP18NG\u002BvqEHh4b\u002BNK/1RzylN3Vz21bENEWKSmFiJgyPOMDhI43Gc1\u002Bemnr\u002BGFDp1uesuIE8APQf3LCh8LpMZvOFwNxycyZ1c9nYPVmtd/U5zmi3CrZcgUhlKgwe88PwH57x/Rc\u002BWyyu64LSA\u002Bgbeu8v9Baswb4tae\u002BOQHDXJs\u002B9bT3LAAC\u002BtbU5Y486GofP\u002B/5TvSIvfHFycuJbwNx6luPhwcH\u002Bd\u002BRyB4Zc9z2DjvtwtzuG12JFTMmYs0m28LgfHO918OKpqpn8t/LFqedWuG7yqg3kvp6eTQezvbsOxivdoylvL4DNAApZrdd8im0lrIgJIadKoXlyyg8f\u002B8jFiaewBl/gfhvI3DC44cBbezNv9ZRzMK2wx9G6kFaqt9uftRqBtTUDTNStnAzEHDru\u002Bz/8\u002BMWLT2ON0sq/uGHLG0Yy\u002BmMbXfdeD2qrXqdU/\u002BUwIsa3tqyUnhJIsRSaIwOuPn6k0XjxC5OTP3y1zINWA3konx95b3//Dqv1XQPafbuI7Cx47uvWpwrd93LT/9svnh//xLe6cGXbh4Ge\u002BzZvuWc45dzf67j3p9e5V\u002ByWpkilFAZP1EJ8db42d\u002BihcnnVQ7J7gfTHbhj7zPZU6o\u002B7UcZroWzMORE8M2PDZ7S1T39zdvbUlyqVywCgPjcy8qa9ufwnc1rdntF6VF/HGb4rMWNt7dH5uYP/WCweXs12HkT/4MPbhh7vc5y3dats14M5a559ZGrqfY9Uq6u6DuOThcL\u002Bu3v7Dg1onetW2a4lC4QNay8YkR\u002BfrQefdm/L5w/ltXPdpYGsVF0ENQguWMEZsThvrb4osurGXs36\u002BWlgVzpeTV7thVrXmolTz58xduxH1qZWu72667qnxerh\u002BKL6rIqn7H9OY6UBN6f1GICxjVm5zT0vUtssticLhdR1mHNzJbMirfWBmTj9IE6taC\u002B8AalCOn1VaRaLzYlARJABkIUgHa/huD8n8ZqL4zMpFhPx44rtTm7B9lSqzwFSNURjWUcEHqKUlBQEXrzu4\u002BHnK15nrMXzYQi3Ya1f0hplCLREOUUu4guToOLLLV\u002BZO3W16xiJzvyp5JYs6vkiqMbpE1WJFglnEDWKOazDT49cqbyI0lf8jqvudHxJbZSf1M7BWpxr1o08rFY5pL0IKuiIGQRVARoQVASYE4uyANMdK9hrISlHolUmAZoq\u002BksrV0ukdWxpxBe8IYrb4uOs8wZ073hLshiCuJxVEdQRdRpFEUyLtFaNayJww45pL4v2gkK0uinxFXgLSVxZwcK8KWBhwDp3TAhBKFF\u002BUeelpBbR6rBBx6Ww8TaSlISw4z3LnZZqWnvyTLl8dJkvX1Idr0znaOWOoR1sif8TX526YGUZ6MhR6sg3S7aBjm0lj9sxlDjPbGH2QCjty4o7L3E1EqVu\u002BGgfDFeigBBa\u002BysIxRUdKhaP3VUYOTekZasDAK9yIAuSPLL28\u002Bg\u002Bjl5HLldnAuSrxa0zRakzncfGcVucVeHHcWtdvowkVenKl1477\u002Brr\u002B5VerW9cnGOzWGdOUCu1IU6YawCt\u002B7q0n1fjvzUQZdI247/XEe3YprRbchgXOAlIZ6Ozi54vWUaRWihyrBaGj5wulf7wB/X6hdd4y2s6FYZz\u002B3pytZyjb3cVeqIfMlje6LozVWZh3NrXlCex6oxbDVGMGgCqiIaOPhbGLdlOiKXz2paKmRWplcLwT75fKj298ogsdDwIKi/Ozn5pIJud11oPWaUKWsFpHeDLjBXwyv19pbglyYtJrJqS/M4AWpdZNxBdqpwM\u002BxqIGkYD7QZqlviszng1rP2u\u002BvPR0Z0DjvN3Gxz37nzHeaydebpwQ7Joo0nCXfL6zkS0zvV/v9XHtndq8trkBwCS1pskIobSfu\u002BVKiAiUjXmuKfU8Wnff8JT6vCpqamXXlhFbtVSPjQyctP2dPrBLPT9w67e3a9U2ot3vY6vdRcAUO2YXSl5r93LyYKzhpH24yQ\u002B0eOOJD8sjFv7\u002BvCFP0wh8fB0cRZzzZijxtqv1YPg248Xi891KzYdUr\u002B3efNeR\u002BQNo573bgXsH3Scm3Iq\u002BhEwpyOZU2FRzLAwbsArz6jJ8QZ0nDWk3evbjr8nI5XW8SYLE1zDeEvJGSTJb0u2VTfmmZlK5cFWg3jf8PBtr0ulDg647jt6tN6RgRrJajWgAc/rGGZ17ujWc5WcBheePtunK1mQeRpIu0EIol/ESM4incMvI9IIrZ0VpSqi1GQ1DMcNMO4AP51oNE6fKZWOn1\u002B/HKuWXmD4/Rs33jnqeXvSSr1lUDtbNWTAgxpOK/Q4SmkH0S\u002BsJGnenb15q9GoOCayuFG0h2wL47ZwqGkk6Q0lGffbhtiyAWpa62IlDE8KcHKm2Tx0qlY78VK9fn6tY7PYENC3t69v596enp2eUnuyWu/q17rgKFXwgMG0UjkPqtdRHUmbi\u002BKW/OrLgs4mPiCtLGwUUawUgrgBSEeMWo2j43hrAIFv7awAMzVrf\u002BIAh8/U6986VCq9ACy9kp69e3Bw5I09PQPlMNziAje6WhcKrru5bm1BATkLFFwRV5Te4Kjkug6B0npQK5WNfpoH4lsz2dm7ATIXiFQCEau0vqSUqqS1nq8bU5kNw/HAmFoITA6m00VrzPzp2dnZS/V65eXrLM9oEWdXb\u002B/A7nR6eGsqlZ81ZhTWjmQ9b6zfcQoudM634YhA9SlBj1LoVUr1JuNpA1Epx9mcNBYLII6btNL5xV4KAWOVqgowl9b6cihSK/n\u002BeN3asmg9vcF1X56oVksXwrB2tFot4trMZyzLRqBnWzY7ONrTk9ueTg8Vw7Cggc09WhdcIDfoeRsq1m50RWkL2egoaCXIi1J9UVJf1IV4UdxU0tEG1l60EBsPo5rW2ul4pbgIpWppxynONJvjVWOmoFR5IJ0\u002Beb5SmZ0ol2eu1XXvRERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERNe3/wOxabcDNmswcQAAAABJRU5ErkJggg==","format":"png","height":200,"offset":483,"size":7144,"width":200}]},"build":{"currentObject":-1,"m486Names":false,"m486Numbers":false,"objects":[]},"duration":0,"pauseDuration":0,"rawExtrusion":0,"warmUpDuration":0,"layers":[],"lastDuration":null}}'
48
+ model.update_from_json(json_patch)
49
+
50
+ def test_json_serialization(self):
51
+ with open('object_model/model_duet5mini.json') as fp:
52
+ json_data = json.load(fp)
53
+ model = ObjectModel.from_json(json_data)
54
+ json_text = json.dumps(json_data, sort_keys=True)
55
+ self.assertEqual(json_text, str(model))
56
+
57
+ def test_messages(self):
58
+ from src.dsf.object_model.messages import MessageType
59
+
60
+ model = ObjectModel()
61
+ json_patch = '{"messages":[{"content":"File 0:/gcodes/Veil_Token.gcode will print in 0h 14m plus heating time","time":"2022-12-31T16:42:22.8058935+00:00","type":0}]}'
62
+ model.update_from_json(json_patch)
63
+ self.assertEqual(len(model.messages), 1)
64
+ self.assertEqual(str(model.messages[0].time), "2022-12-31 16:42:22.805893+00:00")
65
+ self.assertEqual(model.messages[0].type, MessageType.Success)
66
+
67
+ json_patch = '{"messages":[]}'
68
+ model.update_from_json(json_patch)
69
+ self.assertEqual(len(model.messages), 0)
70
+
71
+ def test_move_kinematics(self):
72
+ from src.dsf.object_model.move.kinematics import CoreKinematics, DeltaKinematics, KinematicsName
73
+
74
+ model = ObjectModel()
75
+ json_patch = '{"move": {"kinematics": {"name": "delta","deltaRadius": 123}}}'
76
+ model.update_from_json(json_patch)
77
+
78
+ self.assertIsInstance(model.move.kinematics, DeltaKinematics)
79
+ self.assertEqual(model.move.kinematics.name, KinematicsName.delta)
80
+ self.assertEqual(model.move.kinematics.delta_radius, 123)
81
+
82
+ # Switch to CoreXY (eg: M669 K1)
83
+ json_patch = '{"move":{"kinematics":{"forwardMatrix":[[0.5,0.5,0],[0.5,-0.5,0],[0,0,1]],"inverseMatrix":[[1,1,0],[1,-1,0],[0,0,1]],"tiltCorrection":{"correctionFactor":1,"lastCorrections":[],"maxCorrection":1,"screwPitch":0.5,"screwX":[],"screwY":[]},"name":"coreXY","segmentation":null}}}'
84
+ model.update_from_json(json_patch)
85
+ self.assertIsInstance(model.move.kinematics, CoreKinematics)
86
+ self.assertEqual(model.move.kinematics.name, KinematicsName.coreXY)
87
+
88
+ # Switch to linear delta (eg: M669 K3)
89
+ json_patch = '{"move":{"kinematics":{"deltaRadius":105.6,"homedHeight":240,"printRadius":80,"towers":[{"angleCorrection":0,"diagonal":215,"endstopAdjustment":0,"xPos":-91.452,"yPos":-52.8},{"angleCorrection":0,"diagonal":215,"endstopAdjustment":0,"xPos":91.452,"yPos":-52.8},{"angleCorrection":0,"diagonal":215,"endstopAdjustment":0,"xPos":0,"yPos":105.6}],"xTilt":0,"yTilt":0,"name":"delta","segmentation":null}}}'
90
+ model.update_from_json(json_patch)
91
+ self.assertIsInstance(model.move.kinematics, DeltaKinematics)
92
+ self.assertEqual(model.move.kinematics.name, KinematicsName.delta)
93
+ self.assertEqual(model.move.kinematics.delta_radius, 105.6)
94
+
95
+ def test_plugins(self):
96
+ model = ObjectModel()
97
+ self.assertEqual(len(model.plugins), 0)
98
+
99
+ # Plugin installation
100
+ json_patch = '{"plugins":{"ExecOnMcode":{"dsfFiles":["execOnMcode.py","http_endpoints.py","MCodeAction.py","__init__.py"],"dwcFiles":["js/ExecOnMcode.09113059.js","js/ExecOnMcode.09113059.js.gz","js/ExecOnMcode.09113059.js.map","js/ExecOnMcode.09113059.js.map.gz"],"sdFiles":["sys/ExecOnMcode/top-example.py"],"pid":-1,"id":"ExecOnMcode","name":"ExecOnMcode","author":"Lo\u00EFc GRENON","version":"0.2","license":"GPL-3.0-or-later","homepage":"https://github.com/LoicGRENON/DSF_ExecOnMcode_Plugin","tags":[],"dwcVersion":"3.4.5","dwcDependencies":[],"sbcRequired":true,"sbcDsfVersion":"3.4.5","sbcExecutable":"execOnMcode.py","sbcExecutableArguments":null,"sbcExtraExecutables":[],"sbcOutputRedirected":true,"sbcPermissions":["commandExecution","codeInterceptionRead","registerHttpEndpoints","fileSystemAccess","launchProcesses"],"sbcPackageDependencies":[],"sbcPythonDependencies":["dsf-python\u003E=3.4.5"],"sbcPluginDependencies":[],"rrfVersion":null,"data":{}}}}'
101
+ model.update_from_json(json_patch)
102
+ self.assertEqual(len(model.plugins), 1)
103
+ self.assertIsInstance(model.plugins.get('ExecOnMcode'), Plugin)
104
+ self.assertEqual(len(model.plugins['ExecOnMcode'].dsf_files), 4)
105
+ self.assertEqual(len(model.plugins['ExecOnMcode'].sbc_permissions), 5)
106
+ self.assertEqual(model.plugins['ExecOnMcode'].pid, -1)
107
+
108
+ # Plugin start
109
+ json_patch = '{"plugins":{"ExecOnMcode":{"pid":1125}}}'
110
+ model.update_from_json(json_patch)
111
+ self.assertEqual(model.plugins['ExecOnMcode'].pid, 1125)
112
+
113
+ # Plugin removal
114
+ json_patch = '{"plugins":{"ExecOnMcode":null}}'
115
+ model.update_from_json(json_patch)
116
+ self.assertEqual(len(model.plugins), 0)
117
+
118
+ def test_sensors_filament_monitor(self):
119
+ from src.dsf.object_model.sensors.filament_monitors import FilamentMonitorType
120
+
121
+ model = ObjectModel()
122
+ self.assertEqual(len(model.sensors.filament_monitors), 0)
123
+
124
+ # Change filament monitor to Simple (eg: M591 D0 P1 C"io2.in" S1)
125
+ json_patch = '{"sensors":{"filamentMonitors":[{"enabled":true,"status":"ok","type":"simple"}]}}'
126
+ model.update_from_json(json_patch)
127
+ self.assertEqual(len(model.sensors.filament_monitors), 1)
128
+ self.assertEqual(model.sensors.filament_monitors[0].type, FilamentMonitorType.Simple)
129
+
130
+ # Change filament monitor to Pulsed (rg: M591 D0 P7 C"io2.in" S1)
131
+ json_patch = '{"sensors":{"filamentMonitors":[{"calibrated":null,"configured":{"mmPerPulse":1,"percentMax":160,"percentMin":60,"sampleDistance":5},"enabled":true,"status":"ok","type":"pulsed"}]}}'
132
+ model.update_from_json(json_patch)
133
+ self.assertEqual(len(model.sensors.filament_monitors), 1)
134
+ self.assertEqual(model.sensors.filament_monitors[0].type, FilamentMonitorType.Pulsed)
135
+
136
+ def test_user_sessions(self):
137
+ from src.dsf.object_model import AccessLevel, SessionType
138
+
139
+ model = ObjectModel()
140
+ self.assertEqual(len(model.user_sessions), 0)
141
+
142
+ json_patch = '{"userSessions":[{"accessLevel":"readWrite","id":2,"origin":"::ffff:192.168.1.200","originId":-1,"sessionType":"http"}]}'
143
+ model.update_from_json(json_patch)
144
+ self.assertEqual(len(model.user_sessions), 1)
145
+ self.assertEqual(model.user_sessions[0].access_level, AccessLevel.readWrite)
146
+ self.assertEqual(model.user_sessions[0].session_type, SessionType.http)
147
+
148
+ if __name__ == '__main__':
149
+ unittest.main()
@@ -0,0 +1,46 @@
1
+ import threading
2
+ import os
3
+ import pathlib
4
+ import socket
5
+ import time
6
+ import importlib.util
7
+
8
+ here = pathlib.Path(__file__).parent.parent.resolve()
9
+ example_path = here / "examples/send_simple_code.py"
10
+
11
+ spec = importlib.util.spec_from_file_location("send_simple_code", example_path)
12
+ send_simple_code = importlib.util.module_from_spec(spec)
13
+ spec.loader.exec_module(send_simple_code)
14
+
15
+
16
+ def test_send_simple_code(monkeypatch, tmp_path):
17
+ mock_dcs_socket_file = os.path.join(tmp_path, "dsf.socket")
18
+ monkeypatch.setattr(
19
+ "dsf.connections.CommandConnection.connect.__defaults__",
20
+ (mock_dcs_socket_file,),
21
+ )
22
+
23
+ dcs_passed = threading.Event()
24
+
25
+ def mock_dcs():
26
+ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
27
+ server.bind(mock_dcs_socket_file)
28
+ server.listen(1)
29
+ conn, _ = server.accept()
30
+ conn.sendall('{"version":11, "id":"foobar"}'.encode())
31
+ assert conn.recv(1024) == b'{"mode":"Command","version":11}'
32
+ conn.sendall('{"success":true}'.encode())
33
+ assert conn.recv(1024) == b'{"command":"SimpleCode","Code":"M115","Channel":"SBC"}'
34
+ conn.sendall('{"result":"fake code executed", "success":true}'.encode())
35
+ conn.close()
36
+ dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down
37
+
38
+ thread = threading.Thread(target=mock_dcs, daemon=True)
39
+ thread.start()
40
+ time.sleep(1)
41
+
42
+ send_simple_code.send_simple_code()
43
+
44
+ thread.join()
45
+
46
+ assert dcs_passed.is_set()
@@ -0,0 +1,54 @@
1
+ import threading
2
+ import os
3
+ import pathlib
4
+ import socket
5
+ import time
6
+ import importlib.util
7
+
8
+ here = pathlib.Path(__file__).parent.parent.resolve()
9
+ example_path = here / "examples/subscribe_object_model.py"
10
+ spec = importlib.util.spec_from_file_location("subscribe_object_model", example_path)
11
+ subscribe_object_model = importlib.util.module_from_spec(spec)
12
+ spec.loader.exec_module(subscribe_object_model)
13
+
14
+
15
+ def test_subscribe_object_model(monkeypatch, tmp_path):
16
+ mock_dcs_socket_file = os.path.join(tmp_path, "dsf.socket")
17
+ monkeypatch.setattr(
18
+ "dsf.connections.SubscribeConnection.connect.__defaults__",
19
+ (mock_dcs_socket_file,),
20
+ )
21
+
22
+ dcs_passed = threading.Event()
23
+
24
+ def mock_dcs():
25
+ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
26
+ server.bind(mock_dcs_socket_file)
27
+ server.listen(1)
28
+ conn, _ = server.accept()
29
+ conn.sendall('{"version":11, "id":"foobar"}'.encode())
30
+ assert (
31
+ conn.recv(1024)
32
+ == b'{"mode":"Subscribe","version":11,"SubscriptionMode":"Patch","Filter":"","Filters":null}'
33
+ )
34
+ conn.sendall('{"success":true}'.encode())
35
+ conn.sendall('{"boards":"fake-data", "job":"fake-data", "state":"fake-data"}'.encode())
36
+ assert conn.recv(1024) == b'{"command":"Acknowledge"}'
37
+ conn.sendall('{"boards":"some-other-fake-data"}'.encode())
38
+ assert conn.recv(1024) == b'{"command":"Acknowledge"}'
39
+ conn.sendall('{"job":"some-other-fake-data"}'.encode())
40
+ assert conn.recv(1024) == b'{"command":"Acknowledge"}'
41
+ conn.sendall('{"state":"some-other-fake-data"}'.encode())
42
+ assert conn.recv(1024) == b'{"command":"Acknowledge"}'
43
+ conn.close()
44
+ dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down
45
+
46
+ thread = threading.Thread(target=mock_dcs, daemon=True)
47
+ thread.start()
48
+ time.sleep(1)
49
+
50
+ subscribe_object_model.subscribe()
51
+
52
+ thread.join()
53
+
54
+ assert dcs_passed.is_set()
File without changes
File without changes
File without changes
File without changes