quarchpy 2.2.0__py2.py3-none-any.whl → 2.2.1__py2.py3-none-any.whl

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 (331) hide show
  1. quarchpy/.idea/.name +1 -0
  2. quarchpy/.idea/inspectionProfiles/Project_Default.xml +50 -0
  3. quarchpy/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  4. quarchpy/.idea/misc.xml +7 -0
  5. quarchpy/.idea/modules.xml +8 -0
  6. quarchpy/.idea/quarchpy.iml +12 -0
  7. quarchpy/.idea/vcs.xml +6 -0
  8. quarchpy/.idea/workspace.xml +107 -0
  9. quarchpy/__pycache__/__init__.cpython-311.pyc +0 -0
  10. quarchpy/__pycache__/__init__.cpython-312.pyc +0 -0
  11. quarchpy/__pycache__/_version.cpython-311.pyc +0 -0
  12. quarchpy/__pycache__/_version.cpython-312.pyc +0 -0
  13. quarchpy/__pycache__/connection.cpython-311.pyc +0 -0
  14. quarchpy/__pycache__/connection.cpython-312.pyc +0 -0
  15. quarchpy/__pycache__/run.cpython-312.pyc +0 -0
  16. quarchpy/_version.py +1 -1
  17. quarchpy/config_files/__pycache__/__init__.cpython-311.pyc +0 -0
  18. quarchpy/config_files/__pycache__/__init__.cpython-312.pyc +0 -0
  19. quarchpy/config_files/__pycache__/quarch_config_parser.cpython-311.pyc +0 -0
  20. quarchpy/config_files/__pycache__/quarch_config_parser.cpython-312.pyc +0 -0
  21. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1253-01 - Mini SAS Module Config v3.5 c1.3.xml +597 -0
  22. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1253-01 - Mini SAS Module Config v4.000 c1.3.xml +597 -0
  23. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1253-02 - Mini SAS Module Config v3.5 c1.3.xml +597 -0
  24. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1253-02 - Mini SAS Module Config v4.000 c1.3.xml +597 -0
  25. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1253-xx - Mini SAS Module Config v4.003 c1.6.xml +599 -0
  26. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1271-xx - Ethernet Module Config v3.5 c1.2.xml +533 -0
  27. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1271-xx - Ethernet Module Config v4.000 c1.2.xml +533 -0
  28. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1271-xx - Ethernet Module Config v4.100 c1.3.xml +535 -0
  29. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1292-xx - SFP+ Cable Pull Module Config v4.000 c1.1.xml +449 -0
  30. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1309-04 - USB 3.0 Module Config v4.003 c1.2.xml +530 -0
  31. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1309-xx - USB 3.0 Module Config v3.5 c1.1.xml +528 -0
  32. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1309-xx - USB 3.0 Module Config v4.000 c1.1.xml +528 -0
  33. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1366-xx - QSFP Cable Pull Module Config v4.000 c1.1.xml +684 -0
  34. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1383-xx - eSATAp Module Config v4.000 c1.3.xml +542 -0
  35. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1521-02 - Mini SAS HD Module Config v4.000 c1.1.xml +670 -0
  36. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1521-03 - Mini SAS HD Module Config v4.000 c1.1.xml +670 -0
  37. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1521-05 - Mini SAS HD Module Config v4.005 c1.1.xml +672 -0
  38. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1521-05 - Mini SAS HD Module Config v4.007 c1.2.xml +672 -0
  39. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1521-xx - Mini SAS HD Module Config v4.003 c1.5.xml +672 -0
  40. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1663-xx - Quad QSFP Cable Pull Module Config v4.000 c1.1.xml +1523 -0
  41. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1675-05 - Mini SAS HD Module w Triggering Config v4.007 c1.3.xml +673 -0
  42. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1675-xx - Mini SAS HD Module w Triggering Config v4.000 c1.1.xml +673 -0
  43. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1917-xx - Dual SFP+ Cable Pull Module Config v4.000 c1.1.xml +614 -0
  44. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1971-01 - USB TypeC Module Config v4.000 c1.1.xml +637 -0
  45. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL1971-02 - USB TypeC Module Config v4.000 c1.1.xml +641 -0
  46. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2022-xx - RJ-45 Cable Module Config v4.104 c1.3.xml +535 -0
  47. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2058-xx - External PCIe Module Config v4.001 c1.1.xml +696 -0
  48. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2138-01 - SFP28 Cable Pull Module Config v4.000 c1.1.xml +449 -0
  49. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2146-01 - Gen4 OCuLink Cable Module Config v4.001 c1.1.xml +718 -0
  50. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2162-01 - 24G mini SAS HD Cable Break Module Config v4.000 c1.xml +672 -0
  51. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2171-01 - QSFP28 Cable Module Config v4.000 c1.xml +583 -0
  52. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2171-02 - QSFP28 Cable Module Config v4.000 c1.xml +583 -0
  53. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2341-01 - Gen4 External PCIe Cable Module v4.000.xml +625 -0
  54. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2602-xx - Multiprotocol Link Breaker.xml +414 -0
  55. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL2834-xx - -48V DC Breaker Module v5.000 c1.0.xml +374 -0
  56. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Cable Modules/QTL3000-xx - RJ-45 Cable Module v5.000 c1.4.xml +539 -0
  57. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1069-xx - SBB 2.0 Module Config v3.5 c1.1.xml +1614 -0
  58. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-01 - PCIe Card Module v4.000 c1.0.xml +1271 -0
  59. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-02 - PCIe Card Module Config v4.004 c1.2.xml +1289 -0
  60. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-02 - PCIe Card Module Config v4.005 c1.2.xml +1289 -0
  61. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-02 - PCIe Card Module Config v4.100 c1.4.xml +1290 -0
  62. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-04 - PCIe Card Module Config v4.001 c1.1.xml +1297 -0
  63. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1630-04 - PCIe Card Module Config v4.004 c1.2.xml +1301 -0
  64. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-01 - PCIe Card Module w Triggering Config v4.004 c1.2.xml +1290 -0
  65. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-01 - PCIe Card Module w Triggering Config v4.006 c1.3.xml +1291 -0
  66. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-03 - PCIe Card Module w Triggering Config v4.001 c1.1.xml +1298 -0
  67. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-03 - PCIe Card Module w Triggering Config v4.004 c1.2.xml +1302 -0
  68. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-04 - PCIe Card Module w Triggering Config v4.005 c1.5.xml +1307 -0
  69. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1688-05 - PCIe Card Module w Triggering Config v4.005 c1.xml +1307 -0
  70. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1848-01 - PCIe Lite Card Module Config v4.000.xml +219 -0
  71. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1848-02 - PCIe Lite Card Module Config v4.000.xml +286 -0
  72. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1919-01 - PCIe x8 Card Module Config v4.001 c1.4.xml +965 -0
  73. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL1920-01 - PCIe x8 Card Module w Triggering Config v4.000 c1.3.xml +966 -0
  74. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2019-xx - M.2 Horizontal Card Module Config v4.002 c1.1.xml +703 -0
  75. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2034-xx - M.2 M-Key Vertical Module Config v4.001 c1.1.xml +737 -0
  76. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2067-xx - PCIe SFF Module Config v4.000 c1.1.xml +971 -0
  77. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2067-xx - Sanblaze 2 Drive Riser Card Config v4.006 c1.4.xml +1020 -0
  78. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2067-xx - Sanblaze 2 Drive Riser Card Config v4.007 c1.4.xml +1022 -0
  79. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2073-01 - GEN3 PCIe Lite Card Module Config v4.001.xml +286 -0
  80. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2073-01 - PCIe Lite Card Module Config v4.000.xml +286 -0
  81. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2074-01 - GEN3 PCIe HS Card Module v4.005 c1.4.xml +1301 -0
  82. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2074-01 - PCIe Card Module Config CurrentLimit v4.004 c1.2.xml +1301 -0
  83. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2087-xx - PCIe GEN4 Card Module w Triggering Config v4.001 c1.1.xml +1307 -0
  84. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2087-xx - PCIe GEN4 Card Module w Triggering Config v4.001 c1.3.xml +1212 -0
  85. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2087-xx - PCIe GEN4 Card Module w Triggering Config v5.000 c1.4.xml +1212 -0
  86. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2094-01 - Sanblaze Dualport Riser Card Config v4.001 c1.1.xml +773 -0
  87. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2128-xx - PCIe GEN4 Card Module w Triggering Config v4.000 c1.1.xml +1306 -0
  88. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2128-xx - PCIe GEN4 Card Module w Triggering Config v4.001 c1.4.xml +1211 -0
  89. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2135-xx - PCIe GEN4 Card Module w Triggering Config - Inrush Limit v4.001 c1.4.xml +1211 -0
  90. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2161-01 - EDSFF x8 Module Config v4.000 c1.1.xml +773 -0
  91. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2171-xx - EDSFF x8 Module w Triggering Config 4.001 c1.1.xml +785 -0
  92. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2203-01 - Sanblaze Dualport Rack Riser Card Config v4.000 c1.1.xml +715 -0
  93. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2203-01 - Sanblaze Dualport Rack Riser Card Config v4.005 c1.5.xml +715 -0
  94. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2272-01 - GEN4 EDSFF x8 Module Config v4.000 c1.1.xml +771 -0
  95. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2282-01 - Sanblaze Singleport Rack Riser Card Config v4.000 c1.1.xml +666 -0
  96. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2282-01 - Sanblaze Singleport Rack Riser Card Config v4.002 c1.2.xml +666 -0
  97. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2318-01 - SANBlaze U.2 Rack Riser Card Config v4.000 c1.1.xml +715 -0
  98. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2322-03 - GEN4 M.2 M-Key Vertical Breaker Module Config v5.001 c1.3.xml +738 -0
  99. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2322-xx - GEN4 M.2 M-Key Vertical Breaker Module Config v5.000 c1.1.xml +637 -0
  100. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2334-xx - Gen4 EDSFF x4 Card Module Config v4.000 c1.1.xml +615 -0
  101. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2351-xx - GEN4 EDSFF x4 Card Module +Triggering Config v4.000 c1.1.xml +621 -0
  102. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2351-xx - GEN4 EDSFF x4 Card Module +Triggering Config v4.001 c1.2.xml +624 -0
  103. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2357-xx - PCIe GEN5 Card Module Config v5.000 c1.1.xml +1242 -0
  104. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2358-xx - PCIe GEN5 Card Module w Triggering Config v5.000 c1.1.xml +1250 -0
  105. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2378-xx - SANBlaze U.3 Rack Riser Card Config v4.000 c1.1.xml +682 -0
  106. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2378-xx - SANBlaze U.3 Rack Riser Card Config v4.001 c1.2.xml +698 -0
  107. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2395-01 - GEN4 M.2 M-Key Vertical Breaker Module w Triggering Config v5.000 c1.1.xml +647 -0
  108. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2395-02 - GEN4 M.2 M-Key Vertical Breaker Module w Triggering Config v5.001 c1.3.xml +742 -0
  109. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2395-03 - GEN4 M.2 M-Key Vertical Breaker Module w Triggering Config v5.001 c1.3.xml +742 -0
  110. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2396-xx - PCIe GEN5 Card Module w inrush Config v5.000 c1.1.xml +1242 -0
  111. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2403-xx - Gen4 PCIe Lite Module Config v4.000.xml +293 -0
  112. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2515-xx - PCIe GEN4 Card Module w Triggering - Inrush Limit Config v4.001 c1.3.xml +1212 -0
  113. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2652-xx - Gen5 PCIe Lite Module Config v4.000.xml +259 -0
  114. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2652-xx - Gen5 PCIe Lite Module Config v4.003.xml +288 -0
  115. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2658-xx - Gen5 PCIe Lite Module w Inrush Config v4.000.xml +259 -0
  116. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2661-xx - GEN5 PCIe U.3 Breaker Config w Triggering v5.000 c1.1.xml +813 -0
  117. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2662-xx - GEN5 PCIe U.3 Breaker Config v5.000 c1.1.xml +801 -0
  118. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2686-xx - Gen5 EDSFF E3 x4 Breaker Config v5.000.xml +741 -0
  119. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2686-xx - Gen5 EDSFF E3 x4 Breaker Config v5.001 c1.2.xml +799 -0
  120. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2686-xx - Gen5 EDSFF E3 x4 Breaker Config v5.002 c1.4.xml +743 -0
  121. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2686-xx - Gen5 EDSFF E3 x4 Breaker v5.000 c1.1.xml +741 -0
  122. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2692-xx - Gen5 EDSFF E3 x4 Breaker +Triggering Config v5.000.xml +742 -0
  123. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2692-xx - Gen5 EDSFF E3 x4 Breaker w Triggering Config v5.001 c1.2.xml +800 -0
  124. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2692-xx - Gen5 EDSFF E3 x4 Breaker w Triggering v5.000 c1.1.xml +742 -0
  125. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2766-xx - GEN4 EDSFF E1 x8 Breaker Config v5.000.xml +982 -0
  126. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2767-xx - GEN4 EDSFF E3 x8 Breaker Config v5.000.xml +982 -0
  127. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2768-xx - GEN4 EDSFF E3 2T x8 Breaker Config v5.000.xml +982 -0
  128. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2776-xx - GEN4 EDSFF E1 x8 Breaker +Triggering Config v5.000.xml +983 -0
  129. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2777-xx - GEN4 EDSFF E3 x8 Breaker +Triggering Config v5.000.xml +983 -0
  130. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2778-xx - GEN4 EDSFF E3 2T x8 Breaker +Triggering Config v5.000.xml +983 -0
  131. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2798-xx - PCIe GEN5 Card Module w Triggering w Inrush Limit Config v5.000 c1.1.xml +1250 -0
  132. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2804-xx - GEN5 MCIO x4 to U.2 Breaker Config v5.000 c1.1.xml +813 -0
  133. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2805-xx - GEN5 MCIO x4 to U.2 Breaker + Triggering Config v5.000 c1.1.xml +814 -0
  134. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2813-xx - Gen5 AIC to U.2 Breaker v5.002 c1.3.xml +871 -0
  135. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2814-xx - GEN5 AIC to U.2 Breaker + Triggering Config v5.000 c1.1 .xml +807 -0
  136. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2814-xx - Gen5 AIC to U.2 Breaker + Triggering Config v5.002 c1.3.xml +872 -0
  137. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2892-xx - GEN5 EDSFF E1 x4 Breaker.xml +799 -0
  138. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2901-xx - GEN5 M.2 M-Key Horizontal Breaker Config v5.000 c1.1.xml +753 -0
  139. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2902-xx - GEN5 M.2 M-Key Horizontal Breaker w Triggering Config v5.000 c1.1.xml +762 -0
  140. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2902-xx- -Gen5 M.2 Horizontal Breaker +Triggering Config 5.000 c1.1.xml +762 -0
  141. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2925-xx - GEN5 EDSFF E1 x4 Breaker +Triggering.xml +800 -0
  142. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Card Modules/QTL2954-01 - fake delete.xml +947 -0
  143. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1177-xx - HS Module Config v3.5 c1.5.xml +592 -0
  144. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1177-xx - HS Module Config v4.000 c1.5.xml +592 -0
  145. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1177-xx - HS Module Config v4.006 c1.8.xml +594 -0
  146. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1215-xx - Lite Module Config v3.50.xml +203 -0
  147. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1215-xx - Lite Module Config v4.000.xml +203 -0
  148. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1301-xx - HS Lite Module Config v3.50.xml +281 -0
  149. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1301-xx - HS Lite Module Config v4.000.xml +281 -0
  150. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1429-01 - EMC HS Lite Module Config v4.000.xml +281 -0
  151. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1429-02 - EMC HS Lite Module Config v4.002.xml +234 -0
  152. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1623-03 - 12G HS Lite Module Config v4.001.xml +266 -0
  153. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1623-04 - 12G HS Lite Module Config v4.001.xml +266 -0
  154. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1680-xx - SCA2 Lite Module Config v4.001.xml +333 -0
  155. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1689-01 - 12G HS Module Config v4.001 c1.1.xml +594 -0
  156. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1689-04 - 12G HS Module Config v4.002 c1.1.xml +580 -0
  157. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1689-05 - 12G HS Module Config v4.002 c1.1.xml +579 -0
  158. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1743-02 - PCIe SFF Module Config v4.003 c1.3.xml +794 -0
  159. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1743-02 - PCIe SFF Module Config v4.006 c1.4.xml +698 -0
  160. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1743-xx - PCIe SFF Module Config v4.000 c1.1.xml +789 -0
  161. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1753-xx - 12G Lite Module Config v4.000.xml +192 -0
  162. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL1921-01 - EMC 12G HS Lite Module Config v4.000.xml +233 -0
  163. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2207-01 - GEN 4 PCIe U.2 Drive Module.xml +733 -0
  164. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2207-02 - GEN 4 PCIe U.2 Drive Module v5.000.xml +753 -0
  165. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2207-03 - GEN 4 PCIe SFF HS Drive Module Triggering v5.001.xml +763 -0
  166. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2207-xx - GEN 4 PCIe U.2 Drive Module v4.001.xml +751 -0
  167. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2207-xx - GEN 4 PCIe U.2 Drive Module.xml +746 -0
  168. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2245-01 - GEN 4 PCIe U.3 HS Drive Module v4.000.xml +789 -0
  169. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2245-xx - GEN 4 PCIe U.3 HS Drive Module.xml +823 -0
  170. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2266-01 - GEN 4 PCIe SFF HS Drive Module Triggering.xml +743 -0
  171. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2266-02 - GEN 4 PCIe SFF HS Drive Module Triggering v4.002.xml +758 -0
  172. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2266-03 - GEN 4 PCIe SFF HS Drive Module Triggering v5.001.xml +764 -0
  173. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2266-XX - GEN 4 PCIe SFF HS Drive Module Triggering.xml +747 -0
  174. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2270-01 - GEN 4 PCIe U.3 HS Drive Module Triggering v4.000.xml +798 -0
  175. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2270-xx - GEN 4 PCIe U.3 HS Drive Module Triggering.xml +835 -0
  176. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2542-02 - 24G SAS Drive Breaker Module v5.001.xml +530 -0
  177. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2542-xx - 24G SAS Drive Breaker v5.000 c1.1.xml +530 -0
  178. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2543-xx - 24G SAS Drive Breaker +Triggering v5.000 c1.1.xml +539 -0
  179. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2543-xx - 24G SAS Drive Breaker +Triggering v5.001 c1.3.xml +539 -0
  180. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2602-xx - Multiprotocol Link Breaker.xml +414 -0
  181. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2645-xx - Gen5 PCIe U.2 Drive Module v5.000.xml +792 -0
  182. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2645-xx - Gen5 PCIe U.2 Drive Module v5.001.xml +792 -0
  183. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2645-xx - Gen5 PCIe U.2 Drive Module v5.003.xml +793 -0
  184. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2645-xx - Gen5 PCIe U.2 Drive Module v5.007.xml +816 -0
  185. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2645-xx - Gen5 PCIe U.2 Drive Module v5.008.xml +818 -0
  186. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2651-xx - Gen5 PCIe U.2 Drive Module + Triggering v5.000.xml +801 -0
  187. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2651-xx - Gen5 PCIe U.2 Drive Module + Triggering v5.001.xml +801 -0
  188. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2651-xx - Gen5 PCIe U.2 Drive Module + Triggering v5.003.xml +802 -0
  189. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2651-xx - Gen5 PCIe U.2 Drive Module v5.007.xml +825 -0
  190. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2661-xx - Gen5 U.3 Drive Module + Triggering v5.000.xml +813 -0
  191. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2662-xx - Gen5 PCIe U.3 Drive Module v5.000.xml +801 -0
  192. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2757-xx - Gen5 SFF Lite Breaker Module Config v4.000.xml +272 -0
  193. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2804-xx - Gen5 MCIO to U.2 Breaker +Triggering v5.003.xml +818 -0
  194. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2804-xx - Gen5 MCIO to U.2 Breaker v5.001.xml +813 -0
  195. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2805-xx - Gen5 MCIO to U.2 Breaker +Triggering v5.001.xml +814 -0
  196. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2805-xx - Gen5 MCIO to U.2 Breaker +Triggering v5.002.xml +819 -0
  197. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2805-xx - Gen5 MCIO to U.2 Breaker +Triggering v5.003.xml +819 -0
  198. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2892-xx - Gen5 EDSFF E1 x4 Breaker v5.001.xml +799 -0
  199. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2892-xx - Gen5 EDSFF E1 x4 Breaker v5.002.xml +801 -0
  200. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2940-xx - Gen5 EDSFF E3 x8 Breaker v5.000.xml +1105 -0
  201. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/QTL2941-xx - Gen5 EDSFF E3 x8 Breaker +Triggering v5.000.xml +1106 -0
  202. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Drive Modules/Standard Drive Module Config v3.5 c1.1.xml +463 -0
  203. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Switch Modules/QTL1490-xx - SATA Keyed HS Module Config v4.003 c1.6.xml +555 -0
  204. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/breaker/Switch Modules/QTL1490-xx - SATA Keyed HS Module Config v4.006 c1.8.xml +557 -0
  205. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/colors/default-colors.properties +64 -0
  206. quarchpy/connection_specific/QPS/win-amd64/DeviceConfig/colors/qtl2789-colors.properties +2 -0
  207. quarchpy/connection_specific/QPS/win-amd64/InstallType.dat +1 -0
  208. quarchpy/connection_specific/QPS/win-amd64/QuarchPowerStudio.properties.xml +1 -1
  209. quarchpy/connection_specific/QPS/win-amd64/app.jar +0 -0
  210. quarchpy/connection_specific/QPS/win-amd64/qis/help.txt +9 -0
  211. quarchpy/connection_specific/QPS/win-amd64/qis/qis.jar +0 -0
  212. quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/CInterface-2.0.jar +0 -0
  213. quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/QuarchCommon-2.0.jar +0 -0
  214. quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/usb4java-1.3.1.jar +0 -0
  215. quarchpy/connection_specific/QPS/win-amd64/qps_lib/JFXUtilities-1.0.jar +0 -0
  216. quarchpy/connection_specific/QPS/win-amd64/qps_lib/QuarchCommon-2.0.jar +0 -0
  217. quarchpy/connection_specific/QPS/win-amd64/qps_lib/{qis-1.44.jar → qis-1.45.jar} +0 -0
  218. quarchpy/connection_specific/QPS/win-amd64/resources/profiles/3_PHASE_PAM_AC_DEFAULT.rcf +533 -0
  219. quarchpy/connection_specific/QPS/win-amd64/resources/profiles/3_PHASE_PAM_AC_FULL.rcf +881 -0
  220. quarchpy/connection_specific/QPS/win-amd64/resources/profiles/PAM_EXAMPLE_CONFIG.rcf +193 -0
  221. quarchpy/connection_specific/QPS/win-amd64/scriptCommands.txt +2 -2
  222. quarchpy/connection_specific/QPS/win-amd64/whats-new.txt +12 -22
  223. quarchpy/connection_specific/__pycache__/StreamChannels.cpython-311.pyc +0 -0
  224. quarchpy/connection_specific/__pycache__/StreamChannels.cpython-312.pyc +0 -0
  225. quarchpy/connection_specific/__pycache__/__init__.cpython-311.pyc +0 -0
  226. quarchpy/connection_specific/__pycache__/__init__.cpython-312.pyc +0 -0
  227. quarchpy/connection_specific/__pycache__/connection_QIS.cpython-311.pyc +0 -0
  228. quarchpy/connection_specific/__pycache__/connection_QIS.cpython-312.pyc +0 -0
  229. quarchpy/connection_specific/__pycache__/connection_QPS.cpython-311.pyc +0 -0
  230. quarchpy/connection_specific/__pycache__/connection_QPS.cpython-312.pyc +0 -0
  231. quarchpy/connection_specific/__pycache__/connection_ReST.cpython-311.pyc +0 -0
  232. quarchpy/connection_specific/__pycache__/connection_ReST.cpython-312.pyc +0 -0
  233. quarchpy/connection_specific/__pycache__/connection_Serial.cpython-311.pyc +0 -0
  234. quarchpy/connection_specific/__pycache__/connection_Serial.cpython-312.pyc +0 -0
  235. quarchpy/connection_specific/__pycache__/connection_TCP.cpython-311.pyc +0 -0
  236. quarchpy/connection_specific/__pycache__/connection_TCP.cpython-312.pyc +0 -0
  237. quarchpy/connection_specific/__pycache__/connection_USB.cpython-311.pyc +0 -0
  238. quarchpy/connection_specific/__pycache__/connection_USB.cpython-312.pyc +0 -0
  239. quarchpy/connection_specific/__pycache__/mDNS.cpython-311.pyc +0 -0
  240. quarchpy/connection_specific/__pycache__/mDNS.cpython-312.pyc +0 -0
  241. quarchpy/connection_specific/connection_QIS.py +47 -22
  242. quarchpy/connection_specific/connection_QIS.py.bak +1749 -0
  243. quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-312.pyc +0 -0
  244. quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-312.pyc +0 -0
  245. quarchpy/connection_specific/serial/__pycache__/__init__.cpython-311.pyc +0 -0
  246. quarchpy/connection_specific/serial/__pycache__/__init__.cpython-312.pyc +0 -0
  247. quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-311.pyc +0 -0
  248. quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-312.pyc +0 -0
  249. quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-311.pyc +0 -0
  250. quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-312.pyc +0 -0
  251. quarchpy/connection_specific/serial/__pycache__/win32.cpython-311.pyc +0 -0
  252. quarchpy/connection_specific/serial/__pycache__/win32.cpython-312.pyc +0 -0
  253. quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-311.pyc +0 -0
  254. quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  255. quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-311.pyc +0 -0
  256. quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-312.pyc +0 -0
  257. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-311.pyc +0 -0
  258. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-312.pyc +0 -0
  259. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-311.pyc +0 -0
  260. quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-312.pyc +0 -0
  261. quarchpy/debug/SystemTest.py +2 -2
  262. quarchpy/debug/__pycache__/SystemTest.cpython-311.pyc +0 -0
  263. quarchpy/debug/__pycache__/SystemTest.cpython-312.pyc +0 -0
  264. quarchpy/debug/__pycache__/__init__.cpython-311.pyc +0 -0
  265. quarchpy/debug/__pycache__/__init__.cpython-312.pyc +0 -0
  266. quarchpy/debug/__pycache__/module_debug.cpython-311.pyc +0 -0
  267. quarchpy/debug/__pycache__/module_debug.cpython-312.pyc +0 -0
  268. quarchpy/debug/__pycache__/simple_terminal.cpython-311.pyc +0 -0
  269. quarchpy/debug/__pycache__/simple_terminal.cpython-312.pyc +0 -0
  270. quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-311.pyc +0 -0
  271. quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-312.pyc +0 -0
  272. quarchpy/debug/__pycache__/versionCompare.cpython-311.pyc +0 -0
  273. quarchpy/debug/__pycache__/versionCompare.cpython-312.pyc +0 -0
  274. quarchpy/device/__pycache__/__init__.cpython-311.pyc +0 -0
  275. quarchpy/device/__pycache__/__init__.cpython-312.pyc +0 -0
  276. quarchpy/device/__pycache__/device.cpython-311.pyc +0 -0
  277. quarchpy/device/__pycache__/device.cpython-312.pyc +0 -0
  278. quarchpy/device/__pycache__/quarchArray.cpython-311.pyc +0 -0
  279. quarchpy/device/__pycache__/quarchArray.cpython-312.pyc +0 -0
  280. quarchpy/device/__pycache__/quarchPPM.cpython-311.pyc +0 -0
  281. quarchpy/device/__pycache__/quarchPPM.cpython-312.pyc +0 -0
  282. quarchpy/device/__pycache__/quarchQPS.cpython-311.pyc +0 -0
  283. quarchpy/device/__pycache__/quarchQPS.cpython-312.pyc +0 -0
  284. quarchpy/device/__pycache__/scanDevices.cpython-311.pyc +0 -0
  285. quarchpy/device/__pycache__/scanDevices.cpython-312.pyc +0 -0
  286. quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-311.pyc +0 -0
  287. quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-312.pyc +0 -0
  288. quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-311.pyc +0 -0
  289. quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-312.pyc +0 -0
  290. quarchpy/disk_test/__pycache__/__init__.cpython-311.pyc +0 -0
  291. quarchpy/disk_test/__pycache__/__init__.cpython-312.pyc +0 -0
  292. quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-311.pyc +0 -0
  293. quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-312.pyc +0 -0
  294. quarchpy/fio/__pycache__/FIO_interface.cpython-311.pyc +0 -0
  295. quarchpy/fio/__pycache__/FIO_interface.cpython-312.pyc +0 -0
  296. quarchpy/fio/__pycache__/__init__.cpython-311.pyc +0 -0
  297. quarchpy/fio/__pycache__/__init__.cpython-312.pyc +0 -0
  298. quarchpy/iometer/__pycache__/__init__.cpython-311.pyc +0 -0
  299. quarchpy/iometer/__pycache__/__init__.cpython-312.pyc +0 -0
  300. quarchpy/iometer/__pycache__/gen_iometer_template.cpython-311.pyc +0 -0
  301. quarchpy/iometer/__pycache__/gen_iometer_template.cpython-312.pyc +0 -0
  302. quarchpy/iometer/__pycache__/iometerFuncs.cpython-311.pyc +0 -0
  303. quarchpy/iometer/__pycache__/iometerFuncs.cpython-312.pyc +0 -0
  304. quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-311.pyc +0 -0
  305. quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-312.pyc +0 -0
  306. quarchpy/qis/__pycache__/__init__.cpython-311.pyc +0 -0
  307. quarchpy/qis/__pycache__/__init__.cpython-312.pyc +0 -0
  308. quarchpy/qis/__pycache__/qisFuncs.cpython-311.pyc +0 -0
  309. quarchpy/qis/__pycache__/qisFuncs.cpython-312.pyc +0 -0
  310. quarchpy/qis/qisFuncs.py +31 -7
  311. quarchpy/qps/__pycache__/__init__.cpython-311.pyc +0 -0
  312. quarchpy/qps/__pycache__/__init__.cpython-312.pyc +0 -0
  313. quarchpy/qps/__pycache__/qpsFuncs.cpython-311.pyc +0 -0
  314. quarchpy/qps/__pycache__/qpsFuncs.cpython-312.pyc +0 -0
  315. quarchpy/qps/qpsFuncs.py +26 -5
  316. quarchpy/user_interface/__pycache__/__init__.cpython-311.pyc +0 -0
  317. quarchpy/user_interface/__pycache__/__init__.cpython-312.pyc +0 -0
  318. quarchpy/user_interface/__pycache__/user_interface.cpython-311.pyc +0 -0
  319. quarchpy/user_interface/__pycache__/user_interface.cpython-312.pyc +0 -0
  320. quarchpy/utilities/__pycache__/TestCenter.cpython-311.pyc +0 -0
  321. quarchpy/utilities/__pycache__/TestCenter.cpython-312.pyc +0 -0
  322. quarchpy/utilities/__pycache__/TimeValue.cpython-311.pyc +0 -0
  323. quarchpy/utilities/__pycache__/TimeValue.cpython-312.pyc +0 -0
  324. quarchpy/utilities/__pycache__/Version.cpython-311.pyc +0 -0
  325. quarchpy/utilities/__pycache__/Version.cpython-312.pyc +0 -0
  326. quarchpy/utilities/__pycache__/__init__.cpython-311.pyc +0 -0
  327. quarchpy/utilities/__pycache__/__init__.cpython-312.pyc +0 -0
  328. {quarchpy-2.2.0.dist-info → quarchpy-2.2.1.dist-info}/METADATA +2 -2
  329. {quarchpy-2.2.0.dist-info → quarchpy-2.2.1.dist-info}/RECORD +331 -20
  330. {quarchpy-2.2.0.dist-info → quarchpy-2.2.1.dist-info}/WHEEL +1 -1
  331. {quarchpy-2.2.0.dist-info → quarchpy-2.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1749 @@
1
+ import csv
2
+ import socket
3
+ import re
4
+ import time
5
+ import sys
6
+ import os
7
+ import datetime
8
+ import select
9
+ import threading
10
+ import math
11
+ import logging
12
+ import struct
13
+ from io import StringIO
14
+ from quarchpy.user_interface import *
15
+ import xml.etree.ElementTree as ET
16
+ from connection_specific.StreamChannels import StreamGroups
17
+
18
+
19
+ # QisInterface provides a way of connecting to a Quarch backend running at the specified ip address and port, defaults to localhost and 9722
20
+ class QisInterface:
21
+ def __init__(self, host='127.0.0.1', port=9722, connectionMessage=True):
22
+ self.host = host
23
+ self.port = port
24
+ self.maxRxBytes = 4096
25
+ self.sock = None
26
+ self.StreamRunSentSemaphore = threading.Semaphore()
27
+ self.sockSemaphore = threading.Semaphore()
28
+ self.stopFlagList = []
29
+ self.listSemaphore = threading.Semaphore()
30
+ self.deviceList = []
31
+ self.deviceDict = {}
32
+ self.dictSemaphore = threading.Semaphore()
33
+ self.connect(connectionMessage = connectionMessage)
34
+ self.stripesEvent = threading.Event()
35
+
36
+ self.qps_stream_header = None
37
+ self.qps_record_dir_path = None
38
+ self.qps_record_start_time = None
39
+ self.qps_stream_folder_name = None
40
+
41
+ self.module_xml_header = None
42
+ self.streamGroups = None
43
+ self.has_digitals = False
44
+ self.is_multirate = False
45
+
46
+ self.streamSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
47
+ self.streamSock.settimeout(5)
48
+ self.streamSock.connect((self.host, self.port))
49
+ self.pythonVersion = sys.version[0]
50
+ self.cursor = '>'
51
+ #clear packets
52
+ welcomeString = self.streamSock.recv(self.maxRxBytes).rstrip()
53
+
54
+
55
+ def connect(self, connectionMessage = True):
56
+ '''
57
+ Connect() tries to open a socket on the host and port specified in the objects variables
58
+ If successful it returns the backends welcome string. If it fails it returns a string saying unable to connect
59
+ The backend should be running and host and port set before running this function. Normally it should be called at the beggining
60
+ of talking to the backend and left open until finished talking when the disconnect() function should be ran
61
+
62
+ Param:
63
+ connectionMessage: boolean, optional
64
+ Set to False if you don't want a warning message to appear when an instance is already running on that port. Useful when using isQisRunning() from qisFuncs
65
+ '''
66
+ try:
67
+ self.deviceDictSetup('QIS')
68
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
69
+ self.sock.settimeout(5)
70
+ self.sock.connect((self.host, self.port))
71
+
72
+ #clear packets
73
+ try:
74
+ welcomeString = self.sock.recv(self.maxRxBytes).rstrip()
75
+ welcomeString = 'Connected@' + str(self.host) + ':' + str(self.port) + ' ' + '\n ' + str(welcomeString)
76
+ self.deviceDict['QIS'][0:3] = [False, 'Connected', welcomeString]
77
+ return welcomeString
78
+ except Exception as e:
79
+ logging.error('No welcome received. Unable to connect to Quarch backend on specified host and port (' + self.host + ':' + str(self.port) + ')')
80
+ logging.error('Is backend running and host accessible?')
81
+ self.deviceDict['QIS'][0:3] = [True, 'Disconnected', 'Unable to connect to QIS']
82
+ raise e
83
+ except Exception as e:
84
+ self.deviceDictSetup('QIS')
85
+ if connectionMessage:
86
+ logging.error('Unable to connect to Quarch backend on specified host and port (' + self.host + ':' + str(self.port) + ').')
87
+ logging.error('Is backend running and host accessible?')
88
+ self.deviceDict['QIS'][0:3] = [True, 'Disconnected', 'Unable to connect to QIS']
89
+ raise e
90
+
91
+ # Tries to close the socket to specified host and port.
92
+ def disconnect(self):
93
+ res = 'Disconnecting from backend'
94
+ try:
95
+ self.sock.shutdown(socket.SHUT_RDWR)
96
+ self.sock.close()
97
+ self.deviceDict['QIS'][0:3] = [False, "Disconnected", 'Successfully disconnected from QIS']
98
+ except Exception as e:
99
+ exc_type, exc_obj, exc_tb = sys.exc_info()
100
+ fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
101
+ message = 'Unable to end connection. ' + self.host + ':' + str(self.port) + ' \r\n' + str(exc_type) + ' ' + str(fname) + ' ' + str(exc_tb.tb_lineno)
102
+ self.deviceDict['QIS'][0:3] = [True, "Connected", message]
103
+ raise e
104
+ return res
105
+
106
+ def closeConnection(self, sock=None, conString=None):
107
+ if sock == None:
108
+ sock = self.sock
109
+ if conString is None:
110
+ cmd="close"
111
+ else:
112
+ cmd =conString+" close"
113
+
114
+ response = self.sendAndReceiveText(sock, cmd)
115
+ return response
116
+
117
+ def startStream(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration = None, inMemoryData = None):
118
+ self.StreamRunSentSemaphore.acquire()
119
+ self.deviceDictSetup('QIS')
120
+ i = self.deviceMulti(module)
121
+ self.stopFlagList[i] = True
122
+ self.stripesEvent.set()
123
+ self.module_xml_header = None
124
+
125
+ # Create the thread
126
+ t1 = threading.Thread(target=self.startStreamThread, name=module,
127
+ args=(module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration, inMemoryData))
128
+ # Start the thread
129
+ t1.start()
130
+
131
+ # count = 0
132
+ while (self.stripesEvent.is_set()):
133
+ # count += 1 --debugging to show delay
134
+ pass
135
+ # just wait until event is cleared
136
+
137
+ def startStreamQPS(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator):
138
+ self.StreamRunSentSemaphore.acquire()
139
+ self.deviceDictSetup('QIS')
140
+ i = self.deviceMulti(module)
141
+ self.stopFlagList[i] = True
142
+ self.stripesEvent.set()
143
+ self.module_xml_header = None
144
+
145
+ # Create the thread
146
+ t1 = threading.Thread(target=self.startStreamThreadQPS, name=module,
147
+ args=(module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator))
148
+ # Start the thread
149
+ t1.start()
150
+
151
+ # count = 0
152
+ while (self.stripesEvent.is_set()):
153
+ # count += 1 --debugging to show delay
154
+ pass
155
+ # just wait until event is cleared
156
+
157
+ def stopStream(self, module, blocking = True):
158
+ moduleName=module.ConString
159
+ i = self.deviceMulti(moduleName)
160
+ self.stopFlagList[i] = False
161
+ # Wait until the stream thread is finished before returning to user.
162
+ # This means this function will block until the QIS buffer is emptied by the second while
163
+ # loop in startStreanThread. This may take some time, especially at low averaging but
164
+ # should gurantee the data won't be lost and QIS buffer is emptied.
165
+ if blocking:
166
+ running = True
167
+ while running:
168
+ threadNameList = []
169
+ for t1 in threading.enumerate():
170
+ threadNameList.append(t1.name)
171
+ moduleStreaming= module.sendCommand("rec stream?").lower() #checking if module thinks its streaming.
172
+ moduleStreaming2= module.sendCommand("stream?").lower() #checking if the module has told qis it has stopped streaming.
173
+
174
+ if (moduleName in threadNameList or "running" in moduleStreaming or "running" in moduleStreaming2):
175
+ time.sleep(0.1)
176
+
177
+ else:
178
+ running = False
179
+ time.sleep(0.1)
180
+
181
+ def startStreamThread(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator, streamDuration = None, inMemoryData = None):
182
+ # This is the function that is run when t1 is created. It is run in a seperate thread from
183
+ # the main application so streaming can happen without blocking the main application from
184
+ # doing other things. Within this function/thread you have to be very careful not to try
185
+ # and 'communicate' with anything from other threads. If you do, you MUST use a thread safe
186
+ # way of communicating. The thread creates it's own socket and should use that, NOT the objects socket
187
+ # (which some of the comms with module functions will use by default).
188
+
189
+ f = None
190
+ #Start module streaming and then read stream data
191
+ if inMemoryData is not None:
192
+ if not isinstance(inMemoryData, StringIO):
193
+ raise Exception ("Error! The parameter 'inMemoryData' is NOT of type StringIO")
194
+ f = inMemoryData
195
+ else:
196
+ f = open(fileName, 'w')
197
+
198
+ stripes = ['Empty Header']
199
+ #Send stream command so module starts streaming data into the backends buffer
200
+ streamRes = self.sendAndReceiveCmd(self.streamSock, 'rec stream', device=module, betweenCommandDelay = 0)
201
+ #printText(streamRes)
202
+ if ('rec stream : OK' in streamRes):
203
+ if (releaseOnData == False):
204
+ self.StreamRunSentSemaphore.release()
205
+ self.stripesEvent.clear()
206
+ self.deviceDict[module][0:3] = [False, 'Running', 'Stream Running']
207
+ else:
208
+ self.StreamRunSentSemaphore.release()
209
+ self.stripesEvent.clear()
210
+ self.deviceDict[module][0:3] = [True, 'Stopped', module + " couldn't start because " + streamRes]
211
+ return
212
+
213
+ baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
214
+ count=0
215
+ maxTries=10
216
+ while 'Header Not Available' in baseSamplePeriod:
217
+ baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
218
+ time.sleep(0.1)
219
+ count += 1
220
+ if count > maxTries:
221
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'Header not available']
222
+ exit()
223
+ version = self.streamHeaderVersion(device=module, sock=self.streamSock)
224
+ timeStampHeader = datetime.datetime.now().strftime("%H:%M:%S:%f %d/%m/%y")
225
+ formatHeader = self.streamHeaderFormat(device=module, sock=self.streamSock)
226
+ formatHeader = formatHeader.replace(", ", separator)
227
+ f.write(formatHeader + '\n')
228
+
229
+ numStripesPerRead = 4096
230
+ maxFileExceeded = False
231
+ openAttempts = 0
232
+ leftover = 0
233
+ remainingStripes = []
234
+ streamOverrun = False
235
+ streamComplete = False
236
+
237
+ # baseSamplePeriod is a string in the form [int][unit]
238
+ # where the unit can be S,mS,uS,nS
239
+ # we need to convert it to a float number of seconds
240
+ # and we also derive the baseSampleUnits in string and numeric form
241
+ if 'ns' in baseSamplePeriod.lower():
242
+ baseSampleUnitText = 'ns'
243
+ baseSampleUnitExponent = -9
244
+ elif 'us' in baseSamplePeriod.lower():
245
+ baseSampleUnitText = 'us'
246
+ baseSampleUnitExponent = -6
247
+ elif 'ms' in baseSamplePeriod.lower():
248
+ baseSampleUnitText = 'ms'
249
+ baseSampleUnitExponent = -3
250
+ elif 'S' in baseSamplePeriod.lower():
251
+ baseSampleUnitText = 's'
252
+ baseSampleUnitExponent = 0
253
+ else:
254
+ raise ValueError("couldn't decode samplePeriod")
255
+
256
+ baseSamplePeriodS = int(re.search(r'^\d*\.?\d*', baseSamplePeriod).group())*(10**baseSampleUnitExponent)
257
+
258
+ # # TODO: MD Thinks this implements software averaging, is unused and now performed in QIS
259
+ # if streamAverage != None:
260
+ # #Matt converting streamAveraging into number
261
+ # streamAverage = self.convertStreamAverage(streamAverage)
262
+ # stripesPerAverage = float(streamAverage) / (float(baseSamplePeriodS) * 4e-6)
263
+ if inMemoryData is None:
264
+ f = open(fileName, 'a', newline='') #changed from ab to a as all data should be in string format now regardless of py2 or py3
265
+ isRun = True
266
+ while isRun:
267
+ try:
268
+ # Until the event threadRunEvent is set externally to this thread,
269
+ # loop and read from the stream
270
+ i = self.deviceMulti(module)
271
+ while self.stopFlagList[i] and (not streamOverrun) and (not streamComplete):
272
+ #now = time.time()
273
+ streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead)
274
+ newStripes = newStripes.replace(' ', separator)
275
+ if streamOverrun:
276
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun']
277
+ # TODO: MD Why don't we return isEmpty in the tuple, instead of having this confusing test?
278
+ if (removeChar == -6 and len(newStripes) == 6):
279
+ isEmpty = True
280
+ else:
281
+ isEmpty = False
282
+ if isEmpty == False:
283
+ #Writes in file if not too big else stops streaming
284
+ if fileName is not None:
285
+ statInfo = os.stat(fileName)
286
+ fileMB = statInfo.st_size / 1048576
287
+ else:
288
+ fileMB = 1
289
+ try:
290
+ int(fileMaxMB)
291
+ except:
292
+ continue
293
+ if int(fileMB) < int(fileMaxMB):
294
+ if (releaseOnData == True):
295
+ self.StreamRunSentSemaphore.release()
296
+ self.stripesEvent.clear()
297
+ releaseOnData = False
298
+ # TODO: MD Thinks this implements software averaging, is unused and now performed in QIS where required
299
+ if(streamAverage != None):
300
+ leftover, remainingStripes = self.averageStripes(leftover, stripesPerAverage, newStripes[:removeChar], f, remainingStripes)
301
+ else:
302
+ # if we have a fixed streamDuration
303
+ if streamDuration != None:
304
+ # Get the last data line in the file
305
+ lastLine = newStripes.splitlines()[-3] # the last data line is followed by 'eof' and '>'
306
+ lastTime = lastLine.split(separator)[0] # get the first (time) entry
307
+
308
+ # if the last entry is still within the required stream length, write the whole lot
309
+ if int(lastTime) < int(streamDuration/(10**baseSampleUnitExponent)): # < rather than <= because we start at 0
310
+ newStripes = newStripes.replace(' ', separator)
311
+ f.write(newStripes[:removeChar])
312
+
313
+ # else write each line individually until we have reached the desired endpoint
314
+ else:
315
+ for thisLine in newStripes.splitlines()[:-2]:
316
+ lastTime = thisLine.split(separator)[0]
317
+ if int(lastTime) < int(streamDuration/(10**baseSampleUnitExponent)):
318
+ f.write(thisLine + '\r\n') # Put the CR back on the end
319
+ else:
320
+ streamComplete = True
321
+ break
322
+ else:
323
+ newStripes = newStripes.replace(' ', separator)
324
+ f.write(newStripes[:removeChar])
325
+
326
+ else:
327
+ maxFileExceeded = True
328
+ #printText('QisInterface file size exceeded in loop 1- breaking')
329
+ maxFileStatus = self.streamBufferStatus(device=module, sock=self.streamSock)
330
+ f.write('Warning: Max file size exceeded before end of stream.\n')
331
+ f.write('Unrecorded stripes in buffer when file full: ' + maxFileStatus + '.')
332
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'User defined max filesize reached']
333
+ break
334
+ else:
335
+ # there's no stripes in the buffer - it's not filling up fast -
336
+ # sleeps so we don't spam qis with requests (seems to make QIS crash)
337
+ # it might be clever to change the sleep time accoring to the situation
338
+ # e.g. wait longer with higher averaging or lots of no stripes in a row
339
+ time.sleep(0.1)
340
+ streamStatus = self.streamRunningStatus(device=module, sock=self.streamSock)
341
+ if streamOverrun:
342
+ #printText('QisInterface overrun - breaking')
343
+ break
344
+ elif "Stopped" in streamStatus:
345
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'User halted stream']
346
+ break
347
+ #printText('Left while 1')
348
+ self.sendAndReceiveCmd(self.streamSock, 'rec stop', device=module, betweenCommandDelay = 0)
349
+ streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
350
+ while "stopped" not in streamState.lower():
351
+ logging.debug("waiting for stream? to return stopped")
352
+ time.sleep(0.1)
353
+ streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
354
+
355
+ if (not streamOverrun) and (not maxFileExceeded):
356
+ self.deviceDict[module][0:3] = [False, 'Stopped', 'Stream stopped - emptying buffer']
357
+ # print self.streamBufferStatus(device=module, sock=self.streamSock)
358
+ if (not maxFileExceeded):
359
+ #If the backend buffer still has data then keep reading it out
360
+ #printText('Streaming stopped. Emptying data left in QIS buffer to file (' + self.streamBufferStatus(device=module, sock=self.streamSock) + ')')
361
+ streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead)
362
+ # TODO: Why don't we return isEmpty in the tuple, instead of having this confusing test?
363
+ if (removeChar == -6 and len(newStripes) == 6):
364
+ isEmpty = True
365
+ else:
366
+ isEmpty = False
367
+ while isEmpty == False: # if newStripes has length 6 then it only contains 'eof\r\n'
368
+ if inMemoryData is None:
369
+ statInfo = os.stat(fileName)
370
+ fileMB = statInfo.st_size / 1048576
371
+ else:
372
+ fileMB = 1
373
+ try:
374
+ int(fileMaxMB)
375
+ except:
376
+ continue
377
+ if int(fileMB) < int(fileMaxMB):
378
+ if streamComplete != True:
379
+ if(streamAverage != None):
380
+ leftover, remainingStripes = self.averageStripes(leftover, stripesPerAverage, newStripes[:removeChar], f, remainingStripes)
381
+ else:
382
+ newStripes = newStripes.replace(' ',separator)
383
+ f.write(newStripes[:removeChar])
384
+ else:
385
+ if not maxFileExceeded:
386
+ maxFileStatus = self.streamBufferStatus(device=module, sock=self.streamSock)
387
+ maxFileExceeded = True
388
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'User defined max filesize reached']
389
+ break
390
+ #time.sleep(0.01) #reduce speed of loop to stop spamming qis
391
+ streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module, numStripesPerRead, skipStatusCheck=True)
392
+ if removeChar == -6:
393
+ if len(newStripes) == 6:
394
+ isEmpty = True
395
+ if maxFileExceeded:
396
+ f.write(b'Warning: Max file size exceeded before end of stream.\n')
397
+ f.write(b'Unrecorded stripes in buffer when file full: ' + maxFileStatus + '.')
398
+ logging.warning('Max file size exceeded. Some data has not been saved to file: ' + maxFileStatus + '.')
399
+
400
+ #printText('Stripes in buffer now: ' + self.streamBufferStatus(device=module, sock=self.streamSock))
401
+
402
+ if streamOverrun:
403
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun - QIS buffer empty']
404
+ elif not maxFileExceeded:
405
+ self.deviceDict[module][0:3] = [False, 'Stopped', 'Stream stopped']
406
+ time.sleep(0.2)
407
+ isRun = False
408
+ except IOError as err:
409
+ #printText('\n\n!!!!!!!!!!!!!!!!!!!! IO Error in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
410
+ if inMemoryData is None:
411
+ f.close()
412
+ time.sleep(0.5)
413
+ openAttempts += 1
414
+ if openAttempts > 4:
415
+ logging.error('\n\n!!!!!!!!!!!!!!!!!!!! Too many IO Errors in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
416
+ raise err
417
+ finally:
418
+ if inMemoryData is None:
419
+ f.close()
420
+ else:
421
+ inMemoryData = f
422
+
423
+ # This is the function that is ran when t1 is created. It is ran in a seperate thread from
424
+ # the main application so streaming can happen without blocking the main application from
425
+ # doing other things. Within this function/thread you have to be very careful not to try
426
+ # and 'communicate' with anything from other threads. If you do, you MUST use a thread safe
427
+ # way of communicating. The thread creates it's own socket and should use that NOT the objects socket
428
+ # (which some of the comms with module functions will use by default).
429
+
430
+ def startStreamThreadQPS(self, module, fileName, fileMaxMB, streamName, streamAverage, releaseOnData, separator):
431
+
432
+ # Start module streaming and then read stream data
433
+ # self.sendAndReceiveCmd(self.streamSock, 'stream mode resample 10mS', device=module, betweenCommandDelay=0)
434
+ self.sendAndReceiveCmd(self.streamSock, 'stream mode header v3', device=module, betweenCommandDelay=0)
435
+ self.sendAndReceiveCmd(self.streamSock, 'stream mode power enable', device=module, betweenCommandDelay=0)
436
+ self.sendAndReceiveCmd(self.streamSock, 'stream mode power total enable', device=module, betweenCommandDelay=0)
437
+
438
+ self.qps_record_start_time = time.time() * 1000
439
+
440
+ stripes = ['Empty Header']
441
+ # Send stream command so module starts streaming data into the backends buffer
442
+ streamRes = self.sendAndReceiveCmd(self.streamSock, 'rec stream', device=module, betweenCommandDelay=0)
443
+ # printText(streamRes)
444
+ if ('rec stream : OK' in streamRes):
445
+ if (releaseOnData == False):
446
+ self.StreamRunSentSemaphore.release()
447
+ self.stripesEvent.clear()
448
+ self.deviceDict[module][0:3] = [False, 'Running', 'Stream Running']
449
+ else:
450
+ self.StreamRunSentSemaphore.release()
451
+ self.stripesEvent.clear()
452
+ self.deviceDict[module][0:3] = [True, 'Stopped', module + " couldn't start because " + streamRes]
453
+ return
454
+
455
+ # If recording to file then get header for file
456
+ if (fileName is not None):
457
+
458
+ baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
459
+ count = 0
460
+ maxTries = 10
461
+ while 'Header Not Available' in baseSamplePeriod:
462
+ baseSamplePeriod = self.streamHeaderAverage(device=module, sock=self.streamSock)
463
+ time.sleep(0.1)
464
+ count += 1
465
+ if count > maxTries:
466
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'Header not available']
467
+ exit()
468
+ version = self.streamHeaderVersion(device=module, sock=self.streamSock)
469
+
470
+ numStripesPerRead = 4096
471
+ maxFileExceeded = False
472
+ openAttempts = 0
473
+ leftover = 0
474
+ remainingStripes = []
475
+ streamOverrun = False
476
+ # if streamAverage != None:
477
+ # # Matt converting streamAveraging into number
478
+ # streamAverage = self.convertStreamAverage(streamAverage)
479
+ # stripesPerAverage = float(streamAverage) / (float(baseSamplePeriodS) * 4e-6)
480
+
481
+ isRun = True
482
+
483
+ self.create_dir_structure(module, fileName)
484
+
485
+ while isRun:
486
+ try:
487
+ # with open(fileName, 'ab') as f:
488
+ # Until the event threadRunEvent is set externally to this thread,
489
+ # loop and read from the stream
490
+ i = self.deviceMulti(module)
491
+ while self.stopFlagList[i] and (not streamOverrun):
492
+ # now = time.time()
493
+ streamOverrun, removeChar, newStripes = self.streamGetStripesText(self.streamSock, module,
494
+ numStripesPerRead)
495
+ newStripes = newStripes.replace(' ',separator)
496
+
497
+ if streamOverrun:
498
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'Device buffer overrun']
499
+ if (removeChar == -6 and len(newStripes) == 6):
500
+ isEmpty = True
501
+ else:
502
+ isEmpty = False
503
+ if isEmpty == False:
504
+ # Writes in file if not too big else stops streaming
505
+ # Writing multiple stripes
506
+ if "\r\n" in y:
507
+ y = y.split("\r\n")
508
+
509
+ if self.has_digitals:
510
+ # Write qps files for PAM
511
+ for stripes in y:
512
+ if stripes:
513
+ stripe = stripes.split(",")
514
+ self.write_stripe_to_files_PAM(stripe)
515
+ else:
516
+ # Write qps files for PPM
517
+ for stripes in y:
518
+ if stripes:
519
+ stripe = stripes.split(",")
520
+ self.write_stripe_to_files_HD(stripe)
521
+
522
+ else:
523
+ if self.has_digitals:
524
+ # Write qps files for PAM
525
+ for stripes in y:
526
+ if stripes:
527
+ stripe = stripes.split(",")
528
+ self.write_stripe_to_files_PAM(stripe)
529
+ else:
530
+ # Write qps files for PPM
531
+ for stripes in y:
532
+ if stripes:
533
+ stripe = stripes.split(",")
534
+ self.write_stripe_to_files_HD(stripe)
535
+
536
+
537
+ else:
538
+ # there's no stripes in the buffer - it's not filling up fast -
539
+ # sleeps so we don't spam qis with requests (seems to make QIS crash)
540
+ # it might be clever to change the sleep time accoring to the situation
541
+ # e.g. wait longer with higher averaging or lots of no stripes in a row
542
+ time.sleep(0.1)
543
+ streamStatus = self.streamRunningStatus(device=module, sock=self.streamSock)
544
+ if streamOverrun:
545
+ # printText('QisInterface overrun - breaking')
546
+ break
547
+ elif "Stopped" in streamStatus:
548
+ self.deviceDict[module][0:3] = [True, 'Stopped', 'User halted stream']
549
+ break
550
+
551
+ # printText('Left while 1')
552
+ self.sendAndReceiveCmd(self.streamSock, 'rec stop', device=module, betweenCommandDelay=0)
553
+ # streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module, betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
554
+ # while "stopped" not in streamState.lower():
555
+ # logging.debug("waiting for stream? to contained stopped")
556
+ # time.sleep(0.1)
557
+ # streamState = self.sendAndReceiveCmd(self.streamSock, 'stream?', device=module,betweenCommandDelay=0) # use "stream?" rather than "rec stream?" as it checks both QIS AND the device.
558
+
559
+ isRun = False
560
+ except IOError as err:
561
+ # printText('\n\n!!!!!!!!!!!!!!!!!!!! IO Error in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
562
+ time.sleep(0.5)
563
+ openAttempts += 1
564
+ if openAttempts > 4:
565
+ logging.error(
566
+ '\n\n!!!!!!!!!!!!!!!!!!!! Too many IO Errors in QisInterface !!!!!!!!!!!!!!!!!!!!\n\n')
567
+ raise err
568
+
569
+ self.create_index_file()
570
+ if self.has_digitals:
571
+ self.create_index_file_digitals()
572
+
573
+ self.create_qps_file(module)
574
+
575
+ def write_stripe_to_files_HD(self, stripe):
576
+ # Cycle through items in stripe
577
+ for index, item in enumerate(stripe):
578
+ if index == 0:
579
+ continue
580
+ with open(os.path.join(self.qps_record_dir_path, "data000",
581
+ "data000_00" + index - 1 + "_000000000"),
582
+ "a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
583
+
584
+ x = struct.pack(">d", int(item))
585
+ # logging.debug(item, x)
586
+ file1.write(x)
587
+
588
+ def write_stripe_to_files_PAM(self, stripe):
589
+ # Note to reader - List should be ordered 1>x on analogue and digitals
590
+ counter = 0
591
+ for group in self.streamGroups.groups:
592
+ for i, channel in enumerate(group.channels):
593
+ # incrementing here so we skip stripe[0] which is time
594
+ counter += 1
595
+
596
+ x = i
597
+ while len(str(x)) < 3:
598
+ x = "0" + str(x)
599
+
600
+ # Write all in group 0 to analogue
601
+ if group.group_id == 0:
602
+
603
+ with open(os.path.join(self.qps_record_dir_path, "data000",
604
+ "data000_"+x+"_000000000"),
605
+ "a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
606
+ x = struct.pack(">d", int(stripe[counter]))
607
+ # logging.debug(item, x)
608
+ file1.write(x)
609
+ else:
610
+ # Write all in group 1 to digital
611
+ with open(os.path.join(self.qps_record_dir_path, "data101",
612
+ "data101_"+x+"_000000000"),
613
+ "a") as file1:#changed from ab to a as all data should be in string format now regardless of py2 or py3
614
+ x = struct.pack(">d", int(stripe[counter]))
615
+ # logging.debug(item, x)
616
+ file1.write(x)
617
+
618
+ # Query the backend for a list of connected modules. A $scan command is sent to refresh the list of devices,
619
+ # Then a wait occurs while the backend discovers devices (network ones can take a while) and then a list of device name strings is returned
620
+ # The objects connection needs to be opened (connect()) before this is used
621
+ def getDeviceList(self, sock=None):
622
+
623
+ if sock == None:
624
+ sock = self.sock
625
+ devString = self.sendAndReceiveText(sock, '$list')
626
+ devString = devString.replace('>', '')
627
+ devString = devString.replace(r'\d+\) ', '')
628
+ devString = devString.split('\r\n')
629
+ devString = filter(None, devString) #remove empty elements
630
+ return devString
631
+
632
+ def get_list_details(self, sock=None):
633
+ if sock == None:
634
+ sock = self.sock
635
+
636
+ devString = self.sendAndReceiveText(sock, '$list details')
637
+ devString = devString.replace('>', '')
638
+ devString = devString.replace(r'\d+\) ', '')
639
+ devString = devString.split('\r\n')
640
+ devString = [x for x in devString if x] # remove empty elements
641
+ return devString
642
+
643
+ def scanIP(QisConnection, ipAddress):
644
+ """
645
+ Triggers QIS to look at a specific IP address for a quarch module
646
+
647
+ Parameters
648
+ ----------
649
+ QisConnection : QpsInterface
650
+ The interface to the instance of QPS you would like to use for the scan.
651
+ ipAddress : str
652
+ The IP address of the module you are looking for eg '192.168.123.123'
653
+ sleep : int, optional
654
+ This optional variable sleeps to allow the network to scan for the module before allowing new commands to be sent to QIS.
655
+ """
656
+
657
+ logging.debug("Starting QIS IP Address Lookup at " + ipAddress)
658
+ if not ipAddress.lower().__contains__("tcp::"):
659
+ ipAddress = "TCP::" + ipAddress
660
+ response = "No response from QIS Scan"
661
+ try:
662
+ response = QisConnection.sendCmd(cmd="$scan " + ipAddress, expectedResponse=True)
663
+ # valid response is "Located device: 192.168.1.2"
664
+ if "located" in response.lower():
665
+ logging.debug(response)
666
+ # return the valid response
667
+ return response
668
+ else:
669
+ if "startup" not in response.lower():
670
+ logging.warning("No module found at " + ipAddress)
671
+ logging.warning(response)
672
+ return response
673
+
674
+ except Exception as e:
675
+ logging.warning(e)
676
+ if "startup" not in response.lower():
677
+ logging.warning("No module found at " + ipAddress)
678
+
679
+ def GetQisModuleSelection(self, favouriteOnly=True , additionalOptions=['rescan', 'all con types', 'ip scan'], scan=True):
680
+ '''
681
+ Fuction used to list the available deviced to QIS and present them to the user for selection.
682
+
683
+ Returns myDeviceID - Str the connection string used to connect to the selected device.
684
+ '''
685
+ tableHeaders =["Modules"]
686
+ ip_address = None
687
+ favourite = favouriteOnly
688
+ while True:
689
+ printText("Scanning for modules...")
690
+ if scan and ip_address is None:
691
+ foundDevices = self.qis_scan_devices(scan=scan, favouriteOnly=favourite)
692
+ elif scan and ip_address is not None:
693
+ foundDevices = self.qis_scan_devices(scan=scan, favouriteOnly=favourite, ipAddress=ip_address)
694
+
695
+ myDeviceID = listSelection(title="Select a module",message="Select a module",selectionList=foundDevices,
696
+ additionalOptions= additionalOptions, nice=True, tableHeaders=tableHeaders,
697
+ indexReq=True)
698
+ if myDeviceID.lower() == 'rescan':
699
+ favourite = True
700
+ ip_address = None
701
+ continue
702
+ elif myDeviceID.lower() == 'all con types':
703
+ favourite = False
704
+ printText("Displaying all connection types...")
705
+ continue
706
+ elif myDeviceID.lower() == 'ip scan':
707
+ ip_address = requestDialog(title="Please input the IP Address you would like to scan")
708
+ favourite = False
709
+ continue
710
+ break
711
+
712
+ return myDeviceID
713
+
714
+ def qis_scan_devices(self, scan=True, favouriteOnly=True, ipAddress=None):
715
+ deviceList = []
716
+ foundDevices = "1"
717
+ foundDevices2 = "2" # this is used to check if new modules are being discovered or if all have been found.
718
+ scanWait = 2 # The number of seconds waited between the two scans.
719
+
720
+ if scan:
721
+ if ipAddress == None:
722
+ devString = self.sendAndReceiveText(self.sock, '$scan')
723
+ else:
724
+ devString = self.sendAndReceiveText(self.sock, '$scan TCP::' + ipAddress)
725
+ time.sleep(scanWait)
726
+ while foundDevices not in foundDevices2:
727
+ foundDevices = self.sendAndReceiveText(self.sock, '$list')
728
+ time.sleep(scanWait)
729
+ foundDevices2 = self.sendAndReceiveText(self.sock, '$list')
730
+ else:
731
+ foundDevices = self.sendAndReceiveText(self.sock, '$list')
732
+
733
+ if not "no devices found" in foundDevices.lower():
734
+ foundDevices = foundDevices.replace('>', '')
735
+ #foundDevices = foundDevices.replace(r'\d\) ', '')
736
+ # printText('"' + devString + '"')
737
+ foundDevices = foundDevices.split('\r\n')
738
+ #Can't stream over REST! Removing all REST connections.
739
+ tempList= list()
740
+ for item in foundDevices:
741
+ if item is None or "rest" in item.lower() or item == "":
742
+ pass
743
+ else:
744
+ tempList.append(item.split(")")[1].strip())
745
+ foundDevices = tempList
746
+
747
+ #If favourite only is True then only show one connection type for each module connected.
748
+ #First order the devices in preference type and then pick the first con type found for each module.
749
+ if (favouriteOnly):
750
+ foundDevices = self.sortFavourite(foundDevices)
751
+ else:
752
+ foundDevices = ["***No Devices Found***"]
753
+
754
+ return foundDevices
755
+
756
+ def sortFavourite(self, foundDevices):
757
+ index = 0
758
+ sortedFoundDevices = []
759
+ conPref = ["USB", "TCP", "SERIAL", "REST", "TELNET"]
760
+ while len(sortedFoundDevices) != len(foundDevices):
761
+ for device in foundDevices:
762
+ if conPref[index] in device.upper():
763
+ sortedFoundDevices.append(device)
764
+ index += 1
765
+ foundDevices = sortedFoundDevices
766
+ # new dictionary only containing one favourite connection to each device.
767
+ favConFoundDevices = []
768
+ index = 0
769
+ for device in sortedFoundDevices:
770
+ if (favConFoundDevices == [] or not device.split("::")[1] in str(favConFoundDevices)):
771
+ favConFoundDevices.append(device)
772
+ foundDevices = favConFoundDevices
773
+ return foundDevices
774
+
775
+ # Query stream status for a device attached to backend
776
+ # The objects connection needs to be opened (connect()) before this is used
777
+ def streamRunningStatus(self, device, sock=None):
778
+ if sock == None:
779
+ sock = self.sock
780
+ index = 0 # index of relevant line in split string
781
+ streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
782
+ streamStatus = streamStatus.split('\r\n')
783
+ streamStatus[index] = re.sub(r':', '', streamStatus[index]) #remove :
784
+ return streamStatus[index]
785
+
786
+ # Query stream buffer status for a device attached to backend
787
+ # The objects connection needs to be opened (connect()) before this is used
788
+ def streamBufferStatus(self, device, sock=None):
789
+ if sock == None:
790
+ sock = self.sock
791
+ index = 1 # index of relevant line in split string
792
+ streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
793
+ streamStatus = streamStatus.split('\r\n')
794
+ streamStatus[index] = re.sub(r'^Stripes Buffered: ', '', streamStatus[index])
795
+ return streamStatus[index]
796
+
797
+ # TODO: MD - This function should be replaced with a more generic method of accessing the header
798
+ # The return of a string with concatenated value and units should be replaced with something easier to parse
799
+ #
800
+ # Get the averaging used on the last/current stream
801
+ # The objects connection needs to be opened (connect()) before this is used
802
+ def streamHeaderAverage(self, device, sock=None):
803
+ try:
804
+ if sock == None:
805
+ sock = self.sock
806
+ index = 2 # index of relevant line in split string
807
+ streamStatus = self.sendAndReceiveText(sock, sentText='stream text header', device=device)
808
+
809
+ self.qps_stream_header = streamStatus
810
+
811
+ # Check for the header format. If XML, process here
812
+ if (self.isXmlHeader(streamStatus)):
813
+ # Get the basic averaging rate (V3 header)
814
+ xml_root = self.getStreamXmlHeader(device=device, sock=sock)
815
+
816
+ # For QPS streaming, stream header v3 command has already been issued before this
817
+ self.module_xml_header = xml_root
818
+
819
+ # Return the time based averaging string
820
+ device_period = xml_root.find('.//devicePeriod')
821
+ if device_period == None:
822
+ device_period = xml_root.find('.//devicePerioduS')
823
+ if device_period == None:
824
+ device_period = xml_root.find('.//mainPeriod')
825
+ averageStr = device_period.text
826
+ return averageStr
827
+ # For legacy text headers, process here
828
+ else:
829
+ streamStatus = streamStatus.split('\r\n')
830
+ if('Header Not Available' in streamStatus[0]):
831
+ dummy = streamStatus[0] + '. Check stream has been run on device.'
832
+ return dummy
833
+ streamStatus[index] = re.sub(r'^Average: ', '', streamStatus[index])
834
+ avg = streamStatus[index]
835
+ avg = 2 ** int(avg)
836
+ return '{}'.format(avg)
837
+ except Exception as e:
838
+ logging.error(device + ' Unable to get stream average.' + self.host + ':' + str(self.port))
839
+ raise e
840
+
841
+ # Get the version of the stream and convert to string for the specified device
842
+ # The objects connection needs to be opened (connect()) before this is used
843
+ def streamHeaderVersion(self, device, sock=None):
844
+ try:
845
+ if sock == None:
846
+ sock = self.sock
847
+ index = 0 # index of relevant line in split string
848
+ streamStatus = self.sendAndReceiveText(sock,'stream text header', device)
849
+ streamStatus = streamStatus.split('\r\n')
850
+ if 'Header Not Available' in streamStatus[0]:
851
+ str = streamStatus[0] + '. Check stream has been ran on device.'
852
+ logging.error(str)
853
+ return str
854
+ version = re.sub(r'^Version: ', '', streamStatus[index])
855
+ if version == '3':
856
+ version = 'Original PPM'
857
+ elif version == '4':
858
+ version = 'XLC PPM'
859
+ elif version == '5':
860
+ version = 'HD PPM'
861
+ else:
862
+ version = 'Unknown stream version'
863
+ return version
864
+ except Exception as e:
865
+ logging.error(device + ' Unable to get stream version.' + self.host + ':' + str(self.port))
866
+ raise e
867
+
868
+ # Get a header string giving which measurements are returned in the string for the specified device
869
+ # The objects connection needs to be opened (connect()) before this is used
870
+ def streamHeaderFormat(self, device, sock=None):
871
+ try:
872
+ if sock == None:
873
+ sock = self.sock
874
+ index = 1 # index of relevant line in split string STREAM MODE HEADER [?|V1,V2,V3]
875
+ streamStatus = self.sendAndReceiveText(sock,'stream text header', device)
876
+ # Check if this is a new XML form header
877
+ if (self.isXmlHeader (streamStatus)):
878
+ # Get the basic averaging rate (V3 header)
879
+ xml_root = self.getStreamXmlHeader (device=device, sock=sock)
880
+ # Return the time based averaging string
881
+ device_period = xml_root.find('.//devicePeriod')
882
+ time_unit = 'uS'
883
+ if device_period == None:
884
+ device_period = xml_root.find('.//devicePerioduS')
885
+ if device_period == None:
886
+ device_period = xml_root.find('.//mainPeriod')
887
+ if ('ns' in device_period.text):
888
+ time_unit = 'nS'
889
+ averageStr = device_period.text
890
+
891
+ # Time column always first
892
+ formatHeader = 'Time ' + time_unit + ','
893
+ # Find the channels section of each group and iterate through it to add the channel columns
894
+ for group in xml_root.iter():
895
+ if (group.tag == "channels"):
896
+ for chan in group:
897
+ # Avoid children that are not named channels
898
+ if (chan.find('.//name') is not None):
899
+ nameStr = chan.find('.//name').text
900
+ unitStr = chan.find('.//units').text
901
+ formatHeader = formatHeader + nameStr + " " + unitStr + ","
902
+ return formatHeader
903
+ # Handle legacy text headers here
904
+ else:
905
+ streamStatus = streamStatus.split('\r\n')
906
+ if 'Header Not Available' in streamStatus[0]:
907
+ str = streamStatus[0] + '. Check stream has been ran on device.'
908
+ logging.error(str)
909
+ return str
910
+ outputMode = self.sendAndReceiveText(sock,'Config Output Mode?', device)
911
+ powerMode = self.sendAndReceiveText(sock,'stream mode power?', device)
912
+ format = int(re.sub(r'^Format: ', '', streamStatus[index]))
913
+ b0 = 1 #12V_I
914
+ b1 = 1 << 1 #12V_V
915
+ b2 = 1 << 2 #5V_I
916
+ b3 = 1 << 3 #5V_V
917
+ formatHeader = 'StripeNum, Trig, '
918
+ if format & b3:
919
+ if ('3V3' in outputMode):
920
+ formatHeader = formatHeader + '3V3_V,'
921
+ else:
922
+ formatHeader = formatHeader + '5V_V,'
923
+ if format & b2:
924
+ if ('3V3' in outputMode):
925
+ formatHeader = formatHeader + ' 3V3_I,'
926
+ else:
927
+ formatHeader = formatHeader + ' 5V_I,'
928
+
929
+ if format & b1:
930
+ formatHeader = formatHeader + ' 12V_V,'
931
+ if format & b0:
932
+ formatHeader = formatHeader + ' 12V_I'
933
+ if 'Enabled' in powerMode:
934
+ if ('3V3' in outputMode):
935
+ formatHeader = formatHeader + ' 3V3_P'
936
+ else:
937
+ formatHeader = formatHeader + ' 5V_P'
938
+ if ((format & b1) or (format & b0)):
939
+ formatHeader = formatHeader + ' 12V_P'
940
+ return formatHeader
941
+ except Exception as e:
942
+ logging.error(device + ' Unable to get stream format.' + self.host + ':' + '{}'.format(self.port))
943
+ raise e
944
+
945
+ # Get stripes out of the backends stream buffer for the specified device using text commands
946
+ # The objects connection needs to be opened (connect()) before this is used
947
+ def streamGetStripesText(self, sock, device, numStripes=4096, skipStatusCheck=False):
948
+
949
+ bufferStatus = False
950
+ # Allows the status check to be skipped when emptying the buffer after streaming has stopped (saving time)
951
+ if (skipStatusCheck == False):
952
+ streamStatus = self.sendAndReceiveText(sock, 'stream?', device)
953
+ if ('Overrun' in streamStatus) or ('8388608 of 8388608' in streamStatus):
954
+ bufferStatus = True
955
+ stripes = self.sendAndReceiveText(sock, 'stream text all', device, readUntilCursor=True)
956
+ # time.sleep(0.001)
957
+ if stripes[-1:] != self.cursor:
958
+ return "Error no cursor returned."
959
+ else:
960
+ genEndOfFile = 'eof\r\n>'
961
+ if stripes[-6:] == genEndOfFile:
962
+ removeChar = -6
963
+ else:
964
+ removeChar = -1
965
+
966
+ # stripes = stripes.split('\r\n')
967
+ # stripes = filter(None, stripes) #remove empty sting elements
968
+ #printText(stripes)
969
+ return bufferStatus, removeChar, stripes
970
+
971
+ def avgStringFromPwr(self, avgPwrTwo):
972
+ if(avgPwrTwo==0):
973
+ return '0'
974
+ elif(avgPwrTwo==1):
975
+ return '2'
976
+ elif(avgPwrTwo > 1 and avgPwrTwo < 10 ):
977
+ avg = 2 ** int(avgPwrTwo)
978
+ return '{}'.format(avg)
979
+ elif(avgPwrTwo==10):
980
+ return '1k'
981
+ elif(avgPwrTwo==11):
982
+ return '2k'
983
+ elif(avgPwrTwo==12):
984
+ return '4k'
985
+ elif(avgPwrTwo==13):
986
+ return '8k'
987
+ elif(avgPwrTwo==14):
988
+ return '16k'
989
+ elif(avgPwrTwo==15):
990
+ return '32k'
991
+ else:
992
+ return 'Invalid Average Value'
993
+
994
+ # TODO: MD Thinks this implements software averaging, is unused and now performed in QIS
995
+ # Works out average values of timescales longer than max device averaging
996
+ def averageStripes(self, leftover, streamAverage, newStripes, f, remainingStripes = []):
997
+ newString = str(newStripes)
998
+ newList = []
999
+ if remainingStripes == []:
1000
+ newList = newString.split('\r\n')
1001
+ else:
1002
+ newList = remainingStripes
1003
+ newList.extend(newString.split('\r\n'))
1004
+ numElements = newList[0].count(' ') + 1
1005
+ streamTotalAverage = leftover + streamAverage
1006
+ splitList = [] * numElements
1007
+ if len(newList) < streamTotalAverage:
1008
+ remainingStripes = newList[:-1]
1009
+ return leftover, remainingStripes
1010
+ runningAverage = [0] * (len(newList[0].split(' ')) - 2)
1011
+ j = 0
1012
+ z = 1
1013
+ for i in newList[:-1]:
1014
+ splitList = i.split(' ')
1015
+ splitNumbers = [int(x) for x in splitList[2:]]
1016
+ runningAverage = [sum(x) for x in zip(runningAverage, splitNumbers)]
1017
+ if z == math.floor(streamTotalAverage):
1018
+ finalAverage = splitList[0:2] + [str(round(x / streamAverage)) for x in runningAverage]
1019
+ for counter in xrange(len(finalAverage)-1):
1020
+ finalAverage[counter] = finalAverage[counter] + ' '
1021
+ for x in finalAverage:
1022
+ f.write(x)
1023
+ f.write('\r\n')
1024
+ streamTotalAverage += streamAverage
1025
+ j += 1
1026
+ z += 1
1027
+ remainingStripes = newList[int(math.floor(j * streamAverage + leftover)):-1]
1028
+ leftover = (streamTotalAverage - streamAverage) % 1
1029
+ return leftover, remainingStripes
1030
+
1031
+ def deviceMulti(self, device):
1032
+ if (device in self.deviceList):
1033
+ return self.deviceList.index(device)
1034
+ else:
1035
+ self.listSemaphore.acquire()
1036
+ self.deviceList.append(device)
1037
+ self.stopFlagList.append(True)
1038
+ self.listSemaphore.release()
1039
+ return self.deviceList.index(device)
1040
+
1041
+ def deviceDictSetup(self, module):
1042
+ if module in self.deviceDict.keys():
1043
+ return
1044
+ elif module == 'QIS':
1045
+ self.dictSemaphore.acquire()
1046
+ self.deviceDict[module] = [False, 'Disconnected', "No attempt to connect to QIS yet"]
1047
+ self.dictSemaphore.release()
1048
+ else:
1049
+ self.dictSemaphore.acquire()
1050
+ self.deviceDict[module] = [False, 'Stopped', "User hasn't started stream"]
1051
+ self.dictSemaphore.release()
1052
+
1053
+ def streamInterrupt(self):
1054
+ for key in self.deviceDict.keys():
1055
+ if self.deviceDict[key][0]:
1056
+ return True
1057
+ return False
1058
+
1059
+ def interruptList(self):
1060
+ streamIssueList = []
1061
+ for key in self.deviceDict.keys():
1062
+ if self.deviceDict[key][0]:
1063
+ streamIssue = [key]
1064
+ streamIssue.append(self.deviceDict[key][1])
1065
+ streamIssue.append(self.deviceDict[key][2])
1066
+ streamIssueList.append(streamIssue)
1067
+ return streamIssueList
1068
+
1069
+ def waitStop(self):
1070
+ running = 1
1071
+ while running != 0:
1072
+ threadNameList = []
1073
+ for t1 in threading.enumerate():
1074
+ threadNameList.append(t1.name)
1075
+ running = 0
1076
+ for module in self.deviceList:
1077
+ if (module in threadNameList):
1078
+ running += 1
1079
+ time.sleep(0.5)
1080
+ time.sleep(1)
1081
+
1082
+ def convertStreamAverage (self, streamAveraging):
1083
+ returnValue = 32000;
1084
+ if ("k" in streamAveraging):
1085
+ returnValue = streamAveraging.replace("k", "000")
1086
+ else:
1087
+ returnValue = streamAveraging
1088
+
1089
+ return returnValue
1090
+
1091
+ # Pass in a stream header and we check if it is XML or legacy format
1092
+ def isXmlHeader (self, headerText):
1093
+ if('?xml version=' not in headerText):
1094
+ return False;
1095
+ else:
1096
+ return True
1097
+
1098
+ # Internal function. Gets the stream header and parses it into useful information
1099
+ def getStreamXmlHeader (self, device, sock=None):
1100
+ try:
1101
+ if sock == None:
1102
+ sock = self.sock
1103
+
1104
+ # Get the raw data
1105
+ headerData = self.sendAndReceiveText(sock, sentText='stream text header', device=device)
1106
+
1107
+ # The XML can contain the cursor on the end! Trap and remove it here TODO: Needs fixed in the command layer above
1108
+ if ('\r\n>' in headerData):
1109
+ headerData = headerData[:-1]
1110
+
1111
+ # Check for no header (no stream started)
1112
+ if('Header Not Available' in headerData):
1113
+ logging.error(device + ' Stream header not available.' + self.host + ':' + str(self.port))
1114
+ return None;
1115
+
1116
+ # Check for XML format
1117
+ if('?xml version=' not in headerData):
1118
+ logging.error(device + ' Header not in XML form.' + self.host + ':' + str(self.port))
1119
+ return None;
1120
+
1121
+ # Parse XML into structured format
1122
+ xml_root = ET.fromstring(headerData)
1123
+
1124
+ # Check header format is supported by quarchpy
1125
+ versionStr = xml_root.find('.//version').text
1126
+ if ('V3' not in versionStr):
1127
+ logging.error(device + ' Stream header version not compatible: ' + xml_root['version'].text + '.' + self.host + ':' + str(self.port))
1128
+ raise Exception ("Stream header version not supported");
1129
+
1130
+ # Return the XML structure for the code to use
1131
+ return xml_root
1132
+
1133
+ except Exception as e:
1134
+ logging.error(device + ' Exception while parsing stream header XML.' + self.host + ':' + str(self.port))
1135
+ raise e
1136
+
1137
+ def create_dir_structure(self, module, directory=None):
1138
+ """
1139
+ Creates the QPS directory structure and (empty) files to be written to
1140
+
1141
+ I've put a bunch of try-except just to be sure the directory is correctly created.
1142
+ ( There's probably a better way of doing this than this )
1143
+
1144
+ :param: module: String - Module string
1145
+ :param: directory: String - Name of directory for QPS stream (defaults to default recording location if invalid)
1146
+ :return: success: Boolean - Was the file structure created successfully?
1147
+ """
1148
+
1149
+ directory = self.create_qps_directory(directory)
1150
+
1151
+ digital_count = 0
1152
+ non_dig_counter = 0
1153
+ self.streamGroups = StreamGroups()
1154
+ for index, i in enumerate(self.module_xml_header.findall('.//channels')):
1155
+ self.streamGroups.add_group(index)
1156
+ for item in i.findall('.//channel'):
1157
+ self.streamGroups.groups[index].add_channel(item.find(".//name"), item.find(".//group"), item.find(".//dataPosition"))
1158
+ if item.find(".//group").text == "Digital":
1159
+ digital_count += 1
1160
+ self.has_digitals = True
1161
+ else:
1162
+ non_dig_counter += 1
1163
+
1164
+ # Inner folders for analogue and digital signals streaming
1165
+ in_folder_analogue = "data000"
1166
+ try:
1167
+ inner_path_analogues = os.path.join(directory, in_folder_analogue)
1168
+ os.mkdir(inner_path_analogues)
1169
+ except:
1170
+ logging.warning("Failed to make inner directory for analogue signals " + inner_path_analogues)
1171
+ return False
1172
+
1173
+ in_folder_digitals = "data101"
1174
+ if self.has_digitals:
1175
+ try:
1176
+ inner_path_digitals = os.path.join(directory, in_folder_digitals)
1177
+ os.mkdir(inner_path_digitals)
1178
+ except:
1179
+ logging.warning("Failed to make inner directory for digital signals "+ inner_path_digitals)
1180
+ return False
1181
+
1182
+ logging.debug("Steaming to : " + self.qps_record_dir_path)
1183
+
1184
+ logging.debug("Creating qps data files")
1185
+ try:
1186
+ for i in range(non_dig_counter):
1187
+ file_name = "data000_00"+i+"_000000000"
1188
+ f = open(os.path.join(inner_path_analogues, file_name), "w")
1189
+ f.close()
1190
+ for i in range(digital_count):
1191
+ x = i
1192
+ while len(str(x)) < 3:
1193
+ x = "0" + str(x)
1194
+ file_name = "data101_"+x+"_000000000"
1195
+ f = open(os.path.join(inner_path_digitals, file_name), "w")
1196
+ f.close()
1197
+ except:
1198
+ logging.warning("failed to create qps data files for analogue signals")
1199
+ return False
1200
+
1201
+ logging.debug("Finished creating qps data files")
1202
+
1203
+ logging.debug("Creating qps upper level files")
1204
+ try:
1205
+ file_names = ["annotations.xml", "notes.txt", "triggers.txt"]
1206
+ for file_nome in file_names:
1207
+ f = open(os.path.join(self.qps_record_dir_path, file_nome), "w")
1208
+ f.close()
1209
+ except Exception as err:
1210
+ logging.warning("failed to create qps upper level files, "+err)
1211
+ return False
1212
+
1213
+ try:
1214
+ # Adding data000.idx separate as it's written in bytes not normal text
1215
+ f = open(os.path.join(self.qps_record_dir_path, "data000.idx"), "wb")
1216
+ f.close()
1217
+ if digital_count > 0:
1218
+ f = open(os.path.join(self.qps_record_dir_path, "data101.idx"), "wb")
1219
+ f.close()
1220
+ except Exception as err:
1221
+ logging.warning("failed to create data000.idx file, "+err)
1222
+ return False
1223
+
1224
+ logging.debug("Finished creating QPS dir structure")
1225
+
1226
+ return True
1227
+
1228
+ def create_qps_directory(self, directory):
1229
+ folder_name = None
1230
+ # Checking if there was a directory passed; and if it's a valid directory
1231
+ if not directory:
1232
+ directory = os.path.join(os.path.expanduser("~"), "AppData", "Local", "Quarch", "QPS", "Recordings")
1233
+ logging.debug("No directory specified")
1234
+ elif not os.path.isdir(directory):
1235
+ new_dir = os.path.join(str(os.path.expanduser("~"), "AppData", "Local", "Quarch", "QPS", "Recordings"))
1236
+ logging.warning(directory+" was not a valid directory, streaming to default location: \n"+new_dir)
1237
+ directory = new_dir
1238
+ else:
1239
+ # Split the directory into a path of folders
1240
+ folder_name = str(directory).split(os.sep)
1241
+ # last folder name is the name we want
1242
+ folder_name = folder_name[-1]
1243
+ # Make it known to the entire class that the path we're streaming to is the one sent across by the user
1244
+ self.qps_record_dir_path = directory
1245
+
1246
+ # If no folder name for the stream was passed, then default to 'quarchpy_recording' and a timestamp
1247
+ if not folder_name:
1248
+ folder_name = "quarchpy_recording"
1249
+ folder_name = folder_name + "-" + time.time()
1250
+ path = os.path.join(directory, self.qps_stream_folder_name)
1251
+ os.mkdir(path)
1252
+ self.qps_record_dir_path = path
1253
+
1254
+ self.qps_stream_folder_name = folder_name
1255
+
1256
+ return directory
1257
+
1258
+ def create_index_file(self):
1259
+ """
1260
+ Create the necessary index file for QPS data000.idx
1261
+
1262
+ For future revisions, this should be updated if there are file limits on each data file
1263
+ Current implementation assumes only 1 of each data file are made.
1264
+
1265
+ No Return./
1266
+ """
1267
+
1268
+ stream_header_size = -1
1269
+
1270
+ my_byte_array = []
1271
+
1272
+ # tree = ET.ElementTree(ET.fromstring(self.module_xml_header[:-1]))
1273
+ tree = self.module_xml_header
1274
+
1275
+ return_b_array = []
1276
+ outBuffer = []
1277
+ x = 20
1278
+ stream_header_size = 20
1279
+
1280
+ temp_dict = {"channels": 0}
1281
+
1282
+ return_b_array, stream_header_size = self.add_header_to_byte_array(return_b_array, stream_header_size,
1283
+ temp_dict, tree, is_digital=False)
1284
+
1285
+ self.add_header_to_buffer(outBuffer, return_b_array, stream_header_size, temp_dict)
1286
+
1287
+ # Attempting to read the size of the first file in data files
1288
+ file = os.path.join(self.qps_record_dir_path, "data000", "data000_000_000000000")
1289
+ data = None
1290
+ with open(file, "rb") as f:
1291
+ data = f.read() # if you only wanted to read 512 bytes, do .read(512)
1292
+
1293
+ if not data:
1294
+ raise "No data written to file"
1295
+
1296
+ num_records = len(data) / 8
1297
+ logging.debug("num_record = " + num_records)
1298
+ return_b_array.append(int(num_records).to_bytes(4, byteorder='big'))
1299
+
1300
+ start_number = 0
1301
+ logging.debug("start_record = " + start_number)
1302
+ return_b_array.append(start_number.to_bytes(8, byteorder='big'))
1303
+
1304
+ num_records = num_records - 1
1305
+ logging.debug("last_Record_number = "+num_records)
1306
+ return_b_array.append(int(num_records).to_bytes(8, byteorder='big'))
1307
+
1308
+ # Add names of every file in data000 dir here.
1309
+ files = os.listdir(os.path.join(self.qps_record_dir_path, "data000"))
1310
+ for file3 in files:
1311
+ # print(file)
1312
+ item = strToBb(file3, False)
1313
+ # print(item)
1314
+ while len(item) < 32:
1315
+ item.append("\x00")
1316
+ # print(item)
1317
+ return_b_array.append(item)
1318
+
1319
+ with open(os.path.join(self.qps_record_dir_path, "data000.idx"), "ab") as f:
1320
+ for item in outBuffer:
1321
+ # print(item)
1322
+ # print(type(item))
1323
+ f.write(bytes(item))
1324
+ # f.write(outBuffer)
1325
+
1326
+ with open(os.path.join(self.qps_record_dir_path, "data000.idx"), "ab") as f:
1327
+ self.write_b_array_to_idx_file(f, return_b_array)
1328
+
1329
+ def create_index_file_digitals(self):
1330
+ """
1331
+ Create the necessary index file for QPS data101.idx
1332
+
1333
+ For future revisions, this should be updated if there are file limits on each data file
1334
+ Current implementation assumes only 1 of each data file are made.
1335
+
1336
+ No Return.
1337
+ """
1338
+
1339
+ stream_header_size = -1
1340
+ my_byte_array = []
1341
+ tree = self.module_xml_header
1342
+ return_b_array = []
1343
+ outBuffer = []
1344
+ temp_dict = {}
1345
+
1346
+ return_b_array, stream_header_size = self.add_header_to_byte_array(return_b_array, stream_header_size,
1347
+ temp_dict, tree, is_digital=True)
1348
+
1349
+ self.add_header_to_buffer(outBuffer, return_b_array, stream_header_size, temp_dict)
1350
+
1351
+ # Attempting to read the size of the first file in data files
1352
+ file = os.path.join(self.qps_record_dir_path, "data101", "data101_000_000000000")
1353
+ data = None
1354
+ with open(file, "rb") as f:
1355
+ data = f.read() # if you only wanted to read 512 bytes, do .read(512)
1356
+
1357
+ if not data:
1358
+ raise "No data written to file"
1359
+
1360
+ num_records = len(data) / 8
1361
+ logging.debug("num_record = "+ num_records)
1362
+ return_b_array.append(int(num_records).to_bytes(4, byteorder='big'))
1363
+
1364
+ start_number = 0
1365
+ logging.debug("start_record = "+start_number)
1366
+ return_b_array.append(start_number.to_bytes(8, byteorder='big'))
1367
+
1368
+ num_records = num_records - 1
1369
+ logging.debug("last_Record_number = "+ num_records)
1370
+ return_b_array.append(int(num_records).to_bytes(8, byteorder='big'))
1371
+
1372
+ # Add names of every file in data000 dir here.
1373
+ files = os.listdir(os.path.join(self.qps_record_dir_path, "data101"))
1374
+ for file3 in files:
1375
+ # print(file)
1376
+ item = strToBb(file3, False)
1377
+ # print(item)
1378
+ while len(item) < 32:
1379
+ item.append("\x00")
1380
+ # print(item)
1381
+ return_b_array.append(item)
1382
+
1383
+ with open(os.path.join(self.qps_record_dir_path, "data101.idx"), "ab") as f:
1384
+ for item in outBuffer:
1385
+ f.write(bytes(item))
1386
+
1387
+ with open(os.path.join(self.qps_record_dir_path, "data101.idx"), "ab") as f:
1388
+ self.write_b_array_to_idx_file(f, return_b_array)
1389
+
1390
+ def add_header_to_byte_array(self, return_b_array, stream_header_size, temp_dict, tree, is_digital=False):
1391
+ for element in tree:
1392
+ if "legacyVersion" in element.tag:
1393
+ intItem = element.text
1394
+ temp_dict[element.tag] = intItem
1395
+ # my_byte_array.append(int.to_bytes(intItem, 'big'))
1396
+ if "legacyAverage" in element.tag:
1397
+ intItem = element.text
1398
+ temp_dict[element.tag] = intItem
1399
+ # my_byte_array.append(int.to_bytes(intItem, 'big'))
1400
+ if "legacyFormat" in element.tag:
1401
+ intItem = element.text
1402
+ temp_dict[element.tag] = intItem
1403
+ # my_byte_array.append(int.to_bytes(intItem, 'big'))
1404
+ if "mainPeriod" in element.tag:
1405
+ intItem = element.text
1406
+ intItem = intItem[:-2]
1407
+ temp_dict[element.tag] = intItem
1408
+ if "channels" in element.tag:
1409
+ counter = 0
1410
+ for child in element:
1411
+ for child2 in child:
1412
+ if "group" in child2.tag:
1413
+ if is_digital:
1414
+ if str(child2.text).lower() == "digital":
1415
+ counter += 1
1416
+ else:
1417
+ if str(child2.text).lower() != "digital":
1418
+ counter += 1
1419
+
1420
+ temp_dict[element.tag] = counter
1421
+
1422
+ return_b_array = []
1423
+
1424
+ stream_header_size = 20
1425
+
1426
+ # Cycle through all the channels.
1427
+ for child in element:
1428
+
1429
+ if child.tag == "groupId":
1430
+ continue
1431
+
1432
+ if is_digital:
1433
+ # skip channel if we're only looking for digitals
1434
+ if not str(child.find(".//group").text).lower() == "digital":
1435
+ continue
1436
+ else:
1437
+ # skip if we're looking for analogues
1438
+ if str(child.find(".//group").text).lower() == "digital":
1439
+ continue
1440
+
1441
+ # my_byte_array.append(int.to_bytes(5, 'big'))
1442
+ return_b_array.append(int(5).to_bytes(4, byteorder='big'))
1443
+ stream_header_size += 4
1444
+ name = None
1445
+
1446
+ for child2 in child:
1447
+
1448
+ if "group" in child2.tag:
1449
+ my_byte_array = strToBb(str(child2.text))
1450
+ return_b_array.append(my_byte_array)
1451
+ # QPS index file requires name tag come after group tag.
1452
+ return_b_array.append(name)
1453
+ stream_header_size += len(my_byte_array)
1454
+
1455
+ if "name" in child2.tag:
1456
+ my_byte_array = strToBb(str(child2.text))
1457
+ name = my_byte_array
1458
+ stream_header_size += len(my_byte_array)
1459
+
1460
+ if "units" in child2.tag:
1461
+ my_byte_array = strToBb(str(child2.text))
1462
+ return_b_array.append(my_byte_array)
1463
+ stream_header_size += len(my_byte_array)
1464
+
1465
+ """
1466
+ # Unclear if the only thing here is TRUE
1467
+ bb = strToBB( Boolean.toString( cdr.isUsePrefixStr() ));
1468
+ bbList.add(bb);
1469
+ retVal += bb.capacity();
1470
+ """
1471
+ my_byte_array = strToBb(str("true"))
1472
+ return_b_array.append(my_byte_array)
1473
+ stream_header_size += len(my_byte_array)
1474
+
1475
+ if "maxTValue" in child2.tag:
1476
+ my_byte_array = strToBb(str(child2.text))
1477
+ return_b_array.append(my_byte_array)
1478
+ stream_header_size += len(my_byte_array)
1479
+
1480
+ return return_b_array, stream_header_size
1481
+
1482
+ def add_header_to_buffer(self, outBuffer, return_b_array, stream_header_size, temp_dict):
1483
+ number = 2
1484
+ outBuffer.append(number.to_bytes(4, byteorder='big'))
1485
+ logging.debug("indexVersion : "+ number)
1486
+
1487
+ number = 1 if self.has_digitals else 0
1488
+ outBuffer.append(number.to_bytes(4, byteorder='big'))
1489
+ logging.debug("value0 : "+ number)
1490
+ number = stream_header_size
1491
+ outBuffer.append(number.to_bytes(4, byteorder='big'))
1492
+ logging.debug("header_size : "+number)
1493
+ logging.debug("legacyVersion : "+ temp_dict['legacyVersion'])
1494
+ outBuffer.append(int(temp_dict["legacyVersion"]).to_bytes(4, byteorder='big'))
1495
+ logging.debug("legacyAverage : " + temp_dict['legacyAverage'])
1496
+ outBuffer.append(int(temp_dict["legacyAverage"]).to_bytes(4, byteorder='big'))
1497
+ logging.debug("legacyFormat : "+temp_dict['legacyFormat'])
1498
+ outBuffer.append(int(temp_dict["legacyFormat"]).to_bytes(4, byteorder='big'))
1499
+ logging.debug("mainPeriod : "+temp_dict['mainPeriod'])
1500
+ outBuffer.append(int(temp_dict["mainPeriod"]).to_bytes(4, byteorder='big'))
1501
+ logging.debug("channels : "+temp_dict['channels'])
1502
+ outBuffer.append(int(temp_dict["channels"]).to_bytes(4, byteorder='big'))
1503
+ return_b_array.append(int(self.qps_record_start_time).to_bytes(8, byteorder='big'))
1504
+ index_record_state = True
1505
+ logging.debug(int(1))
1506
+ return_b_array.append(int(1).to_bytes(1, byteorder='big'))
1507
+ record_type = 1
1508
+ logging.debug("record type : "+int(index_record_state))
1509
+ return_b_array.append(int(record_type).to_bytes(1, byteorder='big'))
1510
+
1511
+ def write_b_array_to_idx_file(self, f, return_b_array):
1512
+ # print(return_b_array)
1513
+ for item in return_b_array:
1514
+ # print(item)
1515
+ if isinstance(item, int):
1516
+ # 'f.write(str(item).encode())
1517
+ # print(item)
1518
+ f.write(bytes([item]))
1519
+ continue
1520
+ if isinstance(item, bytes):
1521
+ # print(item)
1522
+ f.write(bytes(item))
1523
+ continue
1524
+ if isinstance(item, list):
1525
+ for character in item:
1526
+ if isinstance(character, int):
1527
+ f.write(bytes([character]))
1528
+ continue
1529
+ elif isinstance(item, bytes):
1530
+ f.write(item)
1531
+ continue
1532
+ else:
1533
+ f.write(str(character).encode())
1534
+ continue
1535
+
1536
+ def create_qps_file(self, module):
1537
+ """
1538
+ Creates the end QPS file that is used to open QPS
1539
+
1540
+ :param module: Module QTL number that was used for the stream
1541
+ :return:
1542
+ """
1543
+
1544
+ with open(os.path.join(self.qps_record_dir_path, self.qps_stream_folder_name + ".qps"), "w") as f:
1545
+ x = datetime.datetime.fromtimestamp(self.qps_record_start_time / 1000.0)
1546
+ x = str(x).split(".")
1547
+ x = x[0]
1548
+ x = x.replace("-", " ")
1549
+ f.write("Started: "+x+"\n")
1550
+ f.write("Device: " + module + "\n")
1551
+ f.write("Fixture: \n")
1552
+
1553
+ x = datetime.datetime.now()
1554
+ x = str(x).split(".")
1555
+ x = x[0]
1556
+ x = x.replace("-", " ")
1557
+ f.write("Saved: "+x+ "\n")
1558
+
1559
+
1560
+ def sendCommand(self, cmd, device="", timeout=20,sock=None,readUntilCursor=True, betweenCommandDelay=0.0, expectedResponse=True):
1561
+ '''Send command is used to send a command to QIS and as far as I can see it has no difference than sendAndReceiveCmd'''
1562
+ if expectedResponse is True:
1563
+ if sock == None:
1564
+ sock = self.sock
1565
+ if not (device == ''):
1566
+ self.deviceDictSetup(device)
1567
+ res = self.sendAndReceiveText(sock, cmd, device, readUntilCursor)
1568
+ if (betweenCommandDelay > 0):
1569
+ time.sleep(betweenCommandDelay)
1570
+ # If ends with cursor get rid of it
1571
+ if res[-1:] == '>':
1572
+ res = res[:-3] # remove last three chars - hopefully '\r\n>'
1573
+ return res
1574
+
1575
+ else :
1576
+ self.sendText(sock, cmd, device)
1577
+ return
1578
+
1579
+ # when sending commands to module (as opposed to back end)
1580
+ # If read until cursor is set to True (which is default) then keep reading response until a cursor is returned as the last character of result string
1581
+ # After command is sent wait for betweenCommandDelay which defaults to 0 but can be specified to add a delay between commands
1582
+ # The objects connection needs to be opened (connect()) before this is used
1583
+ def sendCmd(self, device='', cmd='$help', sock=None, readUntilCursor=True, betweenCommandDelay=0.0, expectedResponse = True):
1584
+ '''Send command is used to send a command to QIS and as far as I can see it has no difference than sendAndReceiveCmd'''
1585
+ if expectedResponse is True:
1586
+ res = self.sendAndReceiveCmd(device=device, cmd=cmd, sock=sock, readUntilCursor=readUntilCursor, betweenCommandDelay=betweenCommandDelay)
1587
+ #If ends with cursor get rid of it
1588
+ if res[-1:] == self.cursor:
1589
+ res = res[:-3] #remove last three chars - hopefully '\r\n>'
1590
+ return res
1591
+ else :
1592
+ self.sendText(sock, cmd, device)
1593
+ return
1594
+
1595
+ def sendAndReceiveCmd(self, sock=None, cmd='$help', device='', readUntilCursor=True, betweenCommandDelay=0.0):
1596
+ if sock==None:
1597
+ sock = self.sock
1598
+ if not (device == ''):
1599
+ self.deviceDictSetup(device)
1600
+ res = self.sendAndReceiveText(sock, cmd, device, readUntilCursor)
1601
+ if (betweenCommandDelay > 0):
1602
+ time.sleep(betweenCommandDelay)
1603
+ #If ends with cursor get rid of it
1604
+ if res[-1:] == '>':
1605
+ res = res[:-3] #remove last three chars - hopefully '\r\n>'
1606
+ return cmd + ' : ' + res
1607
+
1608
+ def sendAndReceiveText(self, sock, sentText='$help', device='', readUntilCursor=True):
1609
+ # Send text to QIS
1610
+ # The objects connection needs to be opened (connect()) before this is used
1611
+ # If read until cursor is set to True (which is default) then keep reading response until a cursor is returned as the last character of result string
1612
+
1613
+ # do sendText
1614
+ self.sockSemaphore.acquire()
1615
+ try:
1616
+ #Send Text
1617
+ self.sendText(sock, sentText, device)
1618
+ #Receive Response
1619
+ res = self.receiveText(sock)
1620
+ # Error Check / validate response
1621
+ if len(res) == 0:
1622
+ #logging.error("Empty response from QIS.")
1623
+ self.sendText(sock, "stream?", device)
1624
+ res = self.receiveText(sock)
1625
+ if len(res) != 0:
1626
+ self.sendText(sock, sentText, device)
1627
+ res = self.receiveText(sock)
1628
+ if len(res) == 0:
1629
+ raise (Exception("Empty response from QIS. Sent: " + sentText))
1630
+ else:
1631
+ raise (Exception("Empty response from QIS. Sent: " + sentText))
1632
+
1633
+ if res[0] == self.cursor:
1634
+ logging.warning('Only returned a cursor from QIS. Sent: ' + sentText)
1635
+ if 'Create Socket Fail' == res[0]: # If create socked fail (between QIS and tcp/ip module)
1636
+ logging.warning(res[0])
1637
+ if 'Connection Timeout' == res[0]:
1638
+ logging.warning(res[0])
1639
+ # If reading until a cursor comes back then keep reading until a cursor appears or max tries exceeded
1640
+ if readUntilCursor:
1641
+ maxReads = 1000
1642
+ count = 1
1643
+ # check for cursor at end of read and if not there read again
1644
+ while res[-1:] != self.cursor:
1645
+ res+= self.receiveText(sock) #TODO Confirm this works with multi response CMD Like a $get stats on a large stream with many annos. test with py2 and py3
1646
+ #res.extend(self.rxBytes(sock))
1647
+ count += 1
1648
+ if count >= maxReads:
1649
+ raise Exception(' Count = Error: max reads exceeded before cursor returned')
1650
+ return res
1651
+
1652
+ except Exception as e:
1653
+ #something went wrong during send qis cmd
1654
+ logging.error(e)
1655
+ raise e
1656
+ finally:
1657
+ self.sockSemaphore.release()
1658
+
1659
+
1660
+ def receiveText(self, sock):
1661
+ if self.pythonVersion == '3':
1662
+ res = bytearray()
1663
+ res.extend(self.rxBytes(sock))
1664
+ res = res.decode()
1665
+ else:
1666
+ res = self.rxBytes(sock)
1667
+ return res
1668
+
1669
+ def sendText(self, sock, message='$help', device=''):
1670
+ # Send text to QIS, don't read it's response
1671
+ # The objects connection needs to be opened (connect()) before this is used
1672
+ if device != '':
1673
+ #specialTimeout = '%500000'
1674
+ #message = device + ' ' + specialTimeout + ' ' + message
1675
+ message = device + ' ' + message
1676
+ #printText('Sending: "' + message + '" ' + self.host + ':' + str(self.port))
1677
+
1678
+ if self.pythonVersion == 2:
1679
+ sock.sendall(message + '\r\n')
1680
+ else:
1681
+ convM = message + '\r\n'
1682
+ sock.sendall(convM.encode('utf-8'))
1683
+ return 'Sent:' + message
1684
+
1685
+ def rxBytes(self,sock):
1686
+ #sock.setblocking(0) #make socket non-blocking
1687
+ #printText('rxBytes')
1688
+ maxExceptions=10
1689
+ exceptions=0
1690
+ maxReadRepeats=50
1691
+ readRepeats=0
1692
+ timeout_in_seconds = 10
1693
+ #Keep trying to read bytes until we get some, unless number of read repeads or exceptions is exceeded
1694
+ while True:
1695
+ try:
1696
+ #select.select returns a list of waitable objects which are ready. On windows it has to be sockets.
1697
+ #The first arguement is a list of objects to wait for reading, second writing, third 'exceptional condition'
1698
+ #We only use the read list and our socket to check if it is readable. if no timeout is specified then it blocks until it becomes readable.
1699
+ ready = select.select([sock], [], [], timeout_in_seconds)
1700
+ #time.sleep(0.1)
1701
+ #ready = [1,2]
1702
+ if ready[0]:
1703
+ ret = sock.recv(self.maxRxBytes)
1704
+ #time.sleep(0.1)
1705
+ return ret
1706
+ else:
1707
+ #printText('rxBytes - readRepeats + 1')
1708
+
1709
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1710
+ sock.connect((self.host, self.port))
1711
+ sock.settimeout(5)
1712
+
1713
+ try:
1714
+ welcomeString = self.sock.recv(self.maxRxBytes).rstrip()
1715
+ welcomeString = 'Connected@' + self.host + ':' + str(self.port) + ' ' + '\n ' + welcomeString
1716
+ printText('New Welcome:' + welcomeString)
1717
+ except Exception as e:
1718
+ logging.error('tried and failed to get new welcome')
1719
+ raise e
1720
+
1721
+ readRepeats=readRepeats+1
1722
+ time.sleep(0.5)
1723
+
1724
+ except Exception as e:
1725
+ #printText('rxBytes - exceptions + 1')
1726
+ exceptions=exceptions+1
1727
+ time.sleep(0.5)
1728
+ raise e
1729
+
1730
+ #If read repeats has been exceeded we failed to get any data on this read.
1731
+ # !!! This is likely to break whatever called us !!!
1732
+ if readRepeats >= maxReadRepeats:
1733
+ logging.error('Max read repeats exceeded - returning.')
1734
+ return 'No data received from QIS'
1735
+ #If number of exceptions exceeded then give up by exiting
1736
+ if exceptions >= maxExceptions:
1737
+ logging.error('Max exceptions exceeded - exiting') #exceptions are probably 10035 non-blocking socket could not complete immediatley
1738
+ exit()
1739
+
1740
+
1741
+ def strToBb(string_in, add_length=True):
1742
+ length = len(str(string_in))
1743
+ b_array = []
1744
+ if add_length:
1745
+ b_array.append(length)
1746
+ for character in str(string_in):
1747
+ b_array.append(character)
1748
+
1749
+ return b_array