@aryacode/dsh-engineering 0.0.0-stage → 0.4.0

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 (284) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/LICENSE +21 -0
  3. package/README.md +230 -2
  4. package/cordis.patch.yml +123 -0
  5. package/lib/client.js +3448 -0
  6. package/lib/index.d.ts +2 -0
  7. package/lib/index.js +9 -0
  8. package/locale/en.json +6 -0
  9. package/locale/zh.json +6 -0
  10. package/package.json +94 -3
  11. package/vendor/ftview/README.md +55 -0
  12. package/vendor/ftview/SKILL.md +74 -0
  13. package/vendor/ftview/lib/checks.d.ts +27 -0
  14. package/vendor/ftview/lib/checks.js +39 -0
  15. package/vendor/ftview/lib/core.d.ts +208 -0
  16. package/vendor/ftview/lib/core.js +160 -0
  17. package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
  18. package/vendor/ftview/lib/edit/drafts.js +84 -0
  19. package/vendor/ftview/lib/edit/operations.d.ts +62 -0
  20. package/vendor/ftview/lib/edit/operations.js +126 -0
  21. package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
  22. package/vendor/ftview/lib/edit/prepare.js +133 -0
  23. package/vendor/ftview/lib/edit/tools.d.ts +25 -0
  24. package/vendor/ftview/lib/edit/tools.js +198 -0
  25. package/vendor/ftview/lib/edit.d.ts +30 -0
  26. package/vendor/ftview/lib/edit.js +38 -0
  27. package/vendor/ftview/lib/index.d.ts +31 -0
  28. package/vendor/ftview/lib/index.js +36 -0
  29. package/vendor/ftview/lib/model.d.ts +80 -0
  30. package/vendor/ftview/lib/model.js +267 -0
  31. package/vendor/ftview/lib/session.d.ts +48 -0
  32. package/vendor/ftview/lib/session.js +98 -0
  33. package/vendor/ftview/lib/skill.d.ts +29 -0
  34. package/vendor/ftview/lib/skill.js +43 -0
  35. package/vendor/ftview/lib/studio/discover.d.ts +36 -0
  36. package/vendor/ftview/lib/studio/discover.js +87 -0
  37. package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
  38. package/vendor/ftview/lib/studio/preflight.js +64 -0
  39. package/vendor/ftview/lib/studio/tools.d.ts +15 -0
  40. package/vendor/ftview/lib/studio/tools.js +83 -0
  41. package/vendor/ftview/lib/studio.d.ts +30 -0
  42. package/vendor/ftview/lib/studio.js +37 -0
  43. package/vendor/ftview/lib/tools.d.ts +14 -0
  44. package/vendor/ftview/lib/tools.js +202 -0
  45. package/vendor/l5x/README.md +62 -0
  46. package/vendor/l5x/SKILL.md +81 -0
  47. package/vendor/l5x/lib/approvals.d.ts +50 -0
  48. package/vendor/l5x/lib/approvals.js +93 -0
  49. package/vendor/l5x/lib/client.js +412 -0
  50. package/vendor/l5x/lib/client.js.map +1 -0
  51. package/vendor/l5x/lib/core/errors.d.ts +38 -0
  52. package/vendor/l5x/lib/core/errors.js +48 -0
  53. package/vendor/l5x/lib/core/params.d.ts +49 -0
  54. package/vendor/l5x/lib/core/params.js +39 -0
  55. package/vendor/l5x/lib/core/result.d.ts +46 -0
  56. package/vendor/l5x/lib/core/result.js +21 -0
  57. package/vendor/l5x/lib/core/tool.d.ts +102 -0
  58. package/vendor/l5x/lib/core/tool.js +19 -0
  59. package/vendor/l5x/lib/drafts.d.ts +66 -0
  60. package/vendor/l5x/lib/drafts.js +98 -0
  61. package/vendor/l5x/lib/editing.d.ts +30 -0
  62. package/vendor/l5x/lib/editing.js +42 -0
  63. package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
  64. package/vendor/l5x/lib/engine/acd-edit.js +115 -0
  65. package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
  66. package/vendor/l5x/lib/engine/analysis.js +258 -0
  67. package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
  68. package/vendor/l5x/lib/engine/capabilities.js +40 -0
  69. package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
  70. package/vendor/l5x/lib/engine/change-set.js +446 -0
  71. package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
  72. package/vendor/l5x/lib/engine/configuration.js +42 -0
  73. package/vendor/l5x/lib/engine/details.d.ts +47 -0
  74. package/vendor/l5x/lib/engine/details.js +173 -0
  75. package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
  76. package/vendor/l5x/lib/engine/direct-polling.js +193 -0
  77. package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
  78. package/vendor/l5x/lib/engine/download-review.js +63 -0
  79. package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
  80. package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
  81. package/vendor/l5x/lib/engine/observations.d.ts +41 -0
  82. package/vendor/l5x/lib/engine/observations.js +67 -0
  83. package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
  84. package/vendor/l5x/lib/engine/project-analysis.js +314 -0
  85. package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
  86. package/vendor/l5x/lib/engine/review-evidence.js +49 -0
  87. package/vendor/l5x/lib/engine/review.d.ts +18 -0
  88. package/vendor/l5x/lib/engine/review.js +123 -0
  89. package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
  90. package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
  91. package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
  92. package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
  93. package/vendor/l5x/lib/engine/usage.d.ts +16 -0
  94. package/vendor/l5x/lib/engine/usage.js +92 -0
  95. package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
  96. package/vendor/l5x/lib/engine/xml-regions.js +77 -0
  97. package/vendor/l5x/lib/fs.d.ts +17 -0
  98. package/vendor/l5x/lib/fs.js +36 -0
  99. package/vendor/l5x/lib/index.d.ts +36 -0
  100. package/vendor/l5x/lib/index.js +42 -0
  101. package/vendor/l5x/lib/live.d.ts +48 -0
  102. package/vendor/l5x/lib/live.js +48 -0
  103. package/vendor/l5x/lib/model/aois.d.ts +39 -0
  104. package/vendor/l5x/lib/model/aois.js +112 -0
  105. package/vendor/l5x/lib/model/errors.d.ts +15 -0
  106. package/vendor/l5x/lib/model/errors.js +18 -0
  107. package/vendor/l5x/lib/model/index.d.ts +20 -0
  108. package/vendor/l5x/lib/model/index.js +19 -0
  109. package/vendor/l5x/lib/model/module.d.ts +37 -0
  110. package/vendor/l5x/lib/model/module.js +54 -0
  111. package/vendor/l5x/lib/model/modules.d.ts +24 -0
  112. package/vendor/l5x/lib/model/modules.js +55 -0
  113. package/vendor/l5x/lib/model/parse.d.ts +32 -0
  114. package/vendor/l5x/lib/model/parse.js +116 -0
  115. package/vendor/l5x/lib/model/project.d.ts +15 -0
  116. package/vendor/l5x/lib/model/project.js +31 -0
  117. package/vendor/l5x/lib/model/routines.d.ts +71 -0
  118. package/vendor/l5x/lib/model/routines.js +183 -0
  119. package/vendor/l5x/lib/model/tags.d.ts +51 -0
  120. package/vendor/l5x/lib/model/tags.js +119 -0
  121. package/vendor/l5x/lib/model/types.d.ts +304 -0
  122. package/vendor/l5x/lib/model/types.js +12 -0
  123. package/vendor/l5x/lib/model/udts.d.ts +34 -0
  124. package/vendor/l5x/lib/model/udts.js +81 -0
  125. package/vendor/l5x/lib/model/xml.d.ts +139 -0
  126. package/vendor/l5x/lib/model/xml.js +291 -0
  127. package/vendor/l5x/lib/model/xref.d.ts +28 -0
  128. package/vendor/l5x/lib/model/xref.js +154 -0
  129. package/vendor/l5x/lib/session.d.ts +119 -0
  130. package/vendor/l5x/lib/session.js +236 -0
  131. package/vendor/l5x/lib/skill.d.ts +30 -0
  132. package/vendor/l5x/lib/skill.js +44 -0
  133. package/vendor/l5x/lib/studio-state.d.ts +77 -0
  134. package/vendor/l5x/lib/studio-state.js +79 -0
  135. package/vendor/l5x/lib/studio.d.ts +29 -0
  136. package/vendor/l5x/lib/studio.js +40 -0
  137. package/vendor/l5x/lib/tools/checks.d.ts +19 -0
  138. package/vendor/l5x/lib/tools/checks.js +140 -0
  139. package/vendor/l5x/lib/tools/common.d.ts +41 -0
  140. package/vendor/l5x/lib/tools/common.js +84 -0
  141. package/vendor/l5x/lib/tools/controller.d.ts +21 -0
  142. package/vendor/l5x/lib/tools/controller.js +170 -0
  143. package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
  144. package/vendor/l5x/lib/tools/drafts.js +270 -0
  145. package/vendor/l5x/lib/tools/live.d.ts +21 -0
  146. package/vendor/l5x/lib/tools/live.js +175 -0
  147. package/vendor/l5x/lib/tools/read.d.ts +19 -0
  148. package/vendor/l5x/lib/tools/read.js +245 -0
  149. package/vendor/l5x/lib/tools/references.d.ts +20 -0
  150. package/vendor/l5x/lib/tools/references.js +269 -0
  151. package/vendor/l5x/lib/tools/studio.d.ts +21 -0
  152. package/vendor/l5x/lib/tools/studio.js +193 -0
  153. package/vendor/l5x/poll-worker.mjs +77 -0
  154. package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
  155. package/vendor/l5x/studio-bridge/Program.cs +304 -0
  156. package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
  157. package/vendor/mwi/README.md +60 -0
  158. package/vendor/mwi/SKILL.md +87 -0
  159. package/vendor/mwi/bridge/README.md +8 -0
  160. package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
  161. package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
  162. package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
  163. package/vendor/mwi/bridge/start_bridge.cmd +21 -0
  164. package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
  165. package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
  166. package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
  167. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  168. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
  169. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
  170. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
  171. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
  172. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
  173. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
  174. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
  175. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
  176. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
  177. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
  178. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
  179. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
  180. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
  181. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
  182. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
  183. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
  184. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
  185. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
  186. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
  187. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
  188. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
  189. package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
  190. package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
  191. package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
  192. package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
  193. package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
  194. package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
  195. package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
  196. package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
  197. package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
  198. package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
  199. package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
  200. package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
  201. package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
  202. package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
  203. package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
  204. package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
  205. package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
  206. package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
  207. package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
  208. package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
  209. package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
  210. package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
  211. package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
  212. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
  213. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
  214. package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
  215. package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
  216. package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
  217. package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
  218. package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
  219. package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
  220. package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
  221. package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
  222. package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
  223. package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
  224. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
  225. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
  226. package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
  227. package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
  228. package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
  229. package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
  230. package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
  231. package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
  232. package/vendor/mwi/code/mw_code.py +1258 -0
  233. package/vendor/mwi/legacy/edit-session.js +50 -0
  234. package/vendor/mwi/legacy/fb-insertion.js +74 -0
  235. package/vendor/mwi/legacy/native-code.js +83 -0
  236. package/vendor/mwi/legacy/native-groups.js +75 -0
  237. package/vendor/mwi/legacy/native-structure.js +143 -0
  238. package/vendor/mwi/legacy/native-variables.js +77 -0
  239. package/vendor/mwi/legacy/pou-conversion.js +47 -0
  240. package/vendor/mwi/legacy/pou-package.js +66 -0
  241. package/vendor/mwi/lib/core/bridge.d.ts +71 -0
  242. package/vendor/mwi/lib/core/bridge.js +53 -0
  243. package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
  244. package/vendor/mwi/lib/core/code-engine.js +109 -0
  245. package/vendor/mwi/lib/core/errors.d.ts +37 -0
  246. package/vendor/mwi/lib/core/errors.js +47 -0
  247. package/vendor/mwi/lib/core/params.d.ts +52 -0
  248. package/vendor/mwi/lib/core/params.js +46 -0
  249. package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
  250. package/vendor/mwi/lib/core/ps-bridge.js +127 -0
  251. package/vendor/mwi/lib/core/result.d.ts +45 -0
  252. package/vendor/mwi/lib/core/result.js +20 -0
  253. package/vendor/mwi/lib/core/tool.d.ts +80 -0
  254. package/vendor/mwi/lib/core/tool.js +55 -0
  255. package/vendor/mwi/lib/edit/compare.d.ts +33 -0
  256. package/vendor/mwi/lib/edit/compare.js +70 -0
  257. package/vendor/mwi/lib/edit.d.ts +20 -0
  258. package/vendor/mwi/lib/edit.js +32 -0
  259. package/vendor/mwi/lib/index.d.ts +21 -0
  260. package/vendor/mwi/lib/index.js +33 -0
  261. package/vendor/mwi/lib/read/service.d.ts +117 -0
  262. package/vendor/mwi/lib/read/service.js +229 -0
  263. package/vendor/mwi/lib/read.d.ts +21 -0
  264. package/vendor/mwi/lib/read.js +33 -0
  265. package/vendor/mwi/lib/session/project.d.ts +34 -0
  266. package/vendor/mwi/lib/session/project.js +54 -0
  267. package/vendor/mwi/lib/session/service.d.ts +167 -0
  268. package/vendor/mwi/lib/session/service.js +566 -0
  269. package/vendor/mwi/lib/session/stage.d.ts +84 -0
  270. package/vendor/mwi/lib/session/stage.js +187 -0
  271. package/vendor/mwi/lib/session/workspace.d.ts +52 -0
  272. package/vendor/mwi/lib/session/workspace.js +90 -0
  273. package/vendor/mwi/lib/skill.d.ts +36 -0
  274. package/vendor/mwi/lib/skill.js +56 -0
  275. package/vendor/mwi/lib/tools/edit.d.ts +20 -0
  276. package/vendor/mwi/lib/tools/edit.js +286 -0
  277. package/vendor/mwi/lib/tools/read.d.ts +19 -0
  278. package/vendor/mwi/lib/tools/read.js +161 -0
  279. package/vendor/mwi/lib/tools/session.d.ts +20 -0
  280. package/vendor/mwi/lib/tools/session.js +166 -0
  281. package/vendor/mwi/lib/tools/verify.d.ts +19 -0
  282. package/vendor/mwi/lib/tools/verify.js +85 -0
  283. package/vendor/mwi/lib/verify.d.ts +17 -0
  284. package/vendor/mwi/lib/verify.js +29 -0
@@ -0,0 +1,86 @@
1
+ [
2
+ {
3
+ "id": "startup-initialization",
4
+ "title": "Explicit startup initialization",
5
+ "topic_ids": [
6
+ "initialization",
7
+ "task-order"
8
+ ],
9
+ "use_when": "A startup POU initializes application state through explicitly configured warm/cold system tasks.",
10
+ "assumptions": [
11
+ "G_Ready and G_State already exist as compatible resource globals.",
12
+ "Assign this POU to the intended startup task; retention policy is project-specific.",
13
+ "Do not reset retained production counters or calibration values without a requirement."
14
+ ],
15
+ "declarations": "VAR_EXTERNAL\n G_Ready : BOOL;\n G_State : INT;\nEND_VAR",
16
+ "body": "(* Startup task only: application state, no hardware commands. *)\nG_Ready := FALSE;\nG_State := INT#0;\n",
17
+ "checks": [
18
+ "Confirm the warm/cold task binding.",
19
+ "Set Ready only after the application has actually established its initialization conditions."
20
+ ]
21
+ },
22
+ {
23
+ "id": "request-edge",
24
+ "title": "Request edge with startup suppression",
25
+ "topic_ids": [
26
+ "execute",
27
+ "initialization"
28
+ ],
29
+ "use_when": "Generate one request pulse per deliberate false-to-true transition; suppress an already-high request on the first scan.",
30
+ "assumptions": [
31
+ "These variables are non-retained locals unless the application explicitly requires otherwise.",
32
+ "A request pulse is not a stop command."
33
+ ],
34
+ "declarations": "VAR\n xRequest : BOOL := FALSE;\n xLast : BOOL := FALSE;\n xInitialized : BOOL := FALSE;\n xPulse : BOOL := FALSE;\nEND_VAR",
35
+ "body": "xPulse := FALSE;\nIF NOT xInitialized THEN\n xLast := xRequest;\n xInitialized := TRUE;\nELSE\n xPulse := xRequest AND NOT xLast;\n xLast := xRequest;\nEND_IF;\n",
36
+ "checks": [
37
+ "Connect xRequest to the intended application request.",
38
+ "Call every scan of the owning cyclic task.",
39
+ "Reset/rearm behavior must match the consuming function block."
40
+ ]
41
+ },
42
+ {
43
+ "id": "cyclic-sequence",
44
+ "title": "Nonblocking two-stage handshake",
45
+ "topic_ids": [
46
+ "task-order",
47
+ "fb-results",
48
+ "initialization"
49
+ ],
50
+ "use_when": "Coordinate two application operations using completion and error inputs without waiting in a WHILE loop.",
51
+ "assumptions": [
52
+ "AckA/AckB describe the current operation, not stale completion bits.",
53
+ "Requests and acknowledgments are mapped to real project code separately.",
54
+ "Add a project-approved timeout for each wait state."
55
+ ],
56
+ "declarations": "VAR\n Request : BOOL := FALSE;\n AckA : BOOL := FALSE;\n AckB : BOOL := FALSE;\n OperationError : BOOL := FALSE;\n ResetRequest : BOOL := FALSE;\n StartA : BOOL := FALSE;\n StartB : BOOL := FALSE;\n Complete : BOOL := FALSE;\n Fault : BOOL := FALSE;\n State : INT := INT#0;\nEND_VAR",
57
+ "body": "StartA := FALSE;\nStartB := FALSE;\nComplete := FALSE;\nCASE State OF\n 0:\n IF Request AND NOT Fault THEN State := INT#10; END_IF;\n 10:\n StartA := TRUE;\n IF OperationError THEN State := INT#900;\n ELSIF AckA THEN State := INT#20; END_IF;\n 20:\n StartB := TRUE;\n IF OperationError THEN State := INT#900;\n ELSIF AckB THEN State := INT#30; END_IF;\n 30:\n Complete := TRUE;\n IF NOT Request THEN State := INT#0; END_IF;\n 900:\n Fault := TRUE;\n IF ResetRequest AND NOT Request AND NOT OperationError THEN\n Fault := FALSE;\n State := INT#0;\n END_IF;\nELSE\n Fault := TRUE;\n State := INT#900;\nEND_CASE;\n",
58
+ "checks": [
59
+ "Every wait returns control to the cyclic task.",
60
+ "Define timeout, abort and retry requirements.",
61
+ "Application request outputs are not physical safety outputs."
62
+ ]
63
+ },
64
+ {
65
+ "id": "cyclic-position-reader",
66
+ "title": "Cyclic Enable/Valid position reader",
67
+ "topic_ids": [
68
+ "enable",
69
+ "axis-id",
70
+ "fb-results"
71
+ ],
72
+ "use_when": "Read an existing logical axis position and latch errors without issuing motion commands.",
73
+ "assumptions": [
74
+ "Axis is an existing correctly configured global AXIS_REF.",
75
+ "MC_ReadActualPosition matches the installed firmware interface.",
76
+ "MonitorEnable is controlled by application readiness."
77
+ ],
78
+ "declarations": "VAR_EXTERNAL\n Axis : AXIS_REF;\nEND_VAR\nVAR\n fbPosition : MC_ReadActualPosition;\n MonitorEnable : BOOL := FALSE;\n PositionValid : BOOL := FALSE;\n LastPosition : LREAL := LREAL#0.0;\n ReadFault : BOOL := FALSE;\n LastErrorID : UINT := UINT#0;\nEND_VAR",
79
+ "body": "fbPosition(Axis := Axis, Enable := MonitorEnable);\nPositionValid := fbPosition.Valid;\nIF PositionValid THEN\n LastPosition := fbPosition.Position;\nEND_IF;\nIF fbPosition.Error THEN\n ReadFault := TRUE;\n LastErrorID := fbPosition.ErrorID;\nEND_IF;\n",
80
+ "checks": [
81
+ "Call cyclically; do not equate a retained last position with current validity.",
82
+ "Define explicit fault acknowledgment separately.",
83
+ "Check logical AxisNum and user units before consuming position."
84
+ ]
85
+ }
86
+ ]
@@ -0,0 +1,208 @@
1
+ """Put a POU back from a snapshot.
2
+
3
+ WHY THIS EXISTS. mw_ide_build now detects the moment a POU is destroyed - a container whose size is
4
+ not plausible, which is what a blown resource grid looks like - and names the POU. It then says to
5
+ restore it, and there was no way to do that. A diagnosis with no remedy is only half a fix.
6
+
7
+ WHAT MAKES IT POSSIBLE. The writer already backs up every file it is about to change, into
8
+ snapshots keyed by timestamp, and those snapshots mirror the project tree: a POU write leaves
9
+ ``<snapshot>/POE/<POU>/src.st1``. Measured on this install: 370 such snapshots. A POU's .VB text and
10
+ its .VGR grid are streams inside that ONE container, so restoring the container restores both - one
11
+ file copy, no format knowledge, nothing to get wrong.
12
+
13
+ WHAT IT DOES NOT DO. It does not pick the snapshot for you beyond taking the newest that holds the
14
+ POU, and it does not decide whether restoring is right. A restore can throw away work done since the
15
+ snapshot, so the tool is dry-run by default and reports which snapshot it would use and how it
16
+ compares to what is on disk.
17
+
18
+ The current file is backed up before being replaced, so a restore is itself undoable.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import shutil
23
+ from dataclasses import dataclass
24
+ from datetime import datetime
25
+ from pathlib import Path
26
+
27
+ from .errors import MotionWorksError, NotFound
28
+ from .snapshot import default_backup_dir
29
+
30
+ #: A POU container normally runs 1.5 KB to 60 KB on this project (21 measured, median 13 KB), so
31
+ #: these bound what a container has to look like before it is trusted. A container outside the range
32
+ #: is damaged, and a DAMAGED CONTAINER MUST NOT BE RESTORED OVER A GOOD FILE or used as a snapshot.
33
+ #:
34
+ #: The upper bound is deliberately the same 1 MB that mw_ide_build uses to decide a POU has been
35
+ #: destroyed. Two parts of one plugin disagreeing about what "damaged" means is its own bug: the
36
+ #: diagnosis would name a POU that restore then refused to treat as damaged, and the contradiction
37
+ #: would surface as a confusing refusal rather than a clear one.
38
+ PLAUSIBLE_MIN = 256
39
+ PLAUSIBLE_MAX = 1_000_000
40
+
41
+
42
+ @dataclass
43
+ class Snapshot:
44
+ """One backup directory, and whether it holds the POU being looked for."""
45
+
46
+ path: Path
47
+ taken: str
48
+ pou_file: Path | None = None
49
+
50
+ @property
51
+ def when(self) -> str:
52
+ """The timestamp rendered for a person, falling back to the directory name."""
53
+ try:
54
+ return datetime.strptime(self.taken, "%Y%m%d-%H%M%S").strftime("%Y-%m-%d %H:%M:%S")
55
+ except ValueError:
56
+ return self.taken
57
+
58
+
59
+ def snapshots_for(project_root: Path) -> list[Snapshot]:
60
+ """Every snapshot for a project, newest first."""
61
+ root = default_backup_dir(project_root)
62
+ if not root.is_dir():
63
+ return []
64
+ found = [Snapshot(path=p, taken=p.name) for p in root.iterdir() if p.is_dir()]
65
+ return sorted(found, key=lambda s: s.taken, reverse=True)
66
+
67
+
68
+ def pou_file_in(snapshot: Path, pou: str) -> Path | None:
69
+ """The POU's container inside a snapshot, if that snapshot holds it."""
70
+ for candidate in snapshot.rglob(f"POE/{pou}/src.st1"):
71
+ if candidate.is_file():
72
+ return candidate
73
+ return None
74
+
75
+
76
+ def find_snapshots(project_root: Path, pou: str) -> list[Snapshot]:
77
+ """Snapshots that hold this POU, newest first, each with the file it holds."""
78
+ out = []
79
+ for snap in snapshots_for(project_root):
80
+ found = pou_file_in(snap.path, pou)
81
+ if found is not None:
82
+ out.append(Snapshot(path=snap.path, taken=snap.taken, pou_file=found))
83
+ return out
84
+
85
+
86
+ def current_pou_file(project_root: Path, pou: str) -> Path | None:
87
+ """The live container for a POU."""
88
+ for candidate in project_root.rglob(f"POE/{pou}/src.st1"):
89
+ if candidate.is_file():
90
+ return candidate
91
+ return None
92
+
93
+
94
+ def describe(path: Path | None) -> dict:
95
+ """Size and plausibility of a container, for comparing a snapshot against what is live."""
96
+ if path is None or not path.is_file():
97
+ return {"path": None, "bytes": 0, "plausible": False}
98
+ size = path.stat().st_size
99
+ return {
100
+ "path": str(path),
101
+ "bytes": size,
102
+ "plausible": PLAUSIBLE_MIN <= size <= PLAUSIBLE_MAX,
103
+ }
104
+
105
+
106
+ def restore_pou(project_root: Path, pou: str, which: int = 0, dry_run: bool = True) -> dict:
107
+ """Replace a POU's container with a copy from a snapshot.
108
+
109
+ ``which`` selects among the snapshots holding this POU, 0 being the newest. Returns a report
110
+ rather than raising for the ordinary refusals, because the caller is usually an agent that has
111
+ just been told its POU is damaged and needs to know what happened.
112
+ """
113
+ from .staging import assert_proven
114
+ assert_proven(project_root)
115
+ if current_pou_file(project_root, pou) is None:
116
+ raise NotFound(
117
+ f"no POU named '{pou}' in {project_root}; snapshots cannot restore what is not there. "
118
+ f"Use mw_code_pous to list what exists."
119
+ )
120
+
121
+ candidates = find_snapshots(project_root, pou)
122
+ if not candidates:
123
+ raise NotFound(
124
+ f"no snapshot holds a POU named '{pou}'. Backups live in "
125
+ f"{default_backup_dir(project_root)}; if the project has never been written to through "
126
+ f"this plugin there will be none."
127
+ )
128
+ if which >= len(candidates):
129
+ raise MotionWorksError(
130
+ f"only {len(candidates)} snapshot(s) hold '{pou}' and which={which} was asked for"
131
+ )
132
+
133
+ chosen = candidates[which]
134
+ target = current_pou_file(project_root, pou)
135
+ before = describe(target)
136
+ source = describe(chosen.pou_file)
137
+
138
+ report = {
139
+ "pou": pou,
140
+ "dry_run": dry_run,
141
+ "snapshot": str(chosen.path),
142
+ "snapshot_taken": chosen.when,
143
+ "snapshots_available": len(candidates),
144
+ "before": before,
145
+ "after": source,
146
+ }
147
+
148
+ if not source["plausible"]:
149
+ report["restored"] = False
150
+ report["refused"] = (
151
+ f"the snapshot's copy of {pou} is {source['bytes']} bytes, outside the "
152
+ f"{PLAUSIBLE_MIN} B to {PLAUSIBLE_MAX} B range a real container falls in. Restoring it "
153
+ f"would replace one damaged file with another; try which=1 for an older snapshot."
154
+ )
155
+ return report
156
+
157
+ if dry_run:
158
+ report["restored"] = False
159
+ report["would_restore"] = True
160
+ return report
161
+
162
+ from .ide import ensure_ide_closed
163
+ ensure_ide_closed()
164
+
165
+ # Keep the current file, so a restore can itself be undone.
166
+ keep = target.with_suffix(".before-restore")
167
+ try:
168
+ shutil.copy2(target, keep)
169
+ report["saved_current_to"] = str(keep)
170
+ except OSError as e:
171
+ report["saved_current_to"] = None
172
+ raise MotionWorksError(f"REFUSED: could not back up current file: {e}") from e
173
+
174
+ shutil.copy2(chosen.pou_file, target)
175
+ after = describe(target)
176
+ report["restored"] = after["bytes"] == source["bytes"]
177
+ report["after"] = after
178
+ report["bytes_restored"] = after["bytes"]
179
+
180
+ if not report["restored"]:
181
+ report["refused"] = (
182
+ f"the copy did not take: the file is {after['bytes']} bytes and the snapshot held "
183
+ f"{source['bytes']}"
184
+ )
185
+ return report
186
+
187
+
188
+ def list_restorable(project_root: Path) -> dict:
189
+ """Which POUs have snapshots, and which look damaged right now - the two together."""
190
+ snapshots = snapshots_for(project_root)
191
+ pous: dict[str, int] = {}
192
+ for snap in snapshots:
193
+ for found in snap.path.rglob("POE/*/src.st1"):
194
+ pous[found.parent.name] = pous.get(found.parent.name, 0) + 1
195
+
196
+ damaged = []
197
+ for live in project_root.rglob("POE/*/src.st1"):
198
+ size = live.stat().st_size
199
+ if size < PLAUSIBLE_MIN or size > PLAUSIBLE_MAX:
200
+ damaged.append({"pou": live.parent.name, "bytes": size,
201
+ "snapshots": pous.get(live.parent.name, 0)})
202
+
203
+ return {
204
+ "snapshots": len(snapshots),
205
+ "restorable_pous": len(pous),
206
+ "most_recent": snapshots[0].when if snapshots else None,
207
+ "damaged": damaged,
208
+ }