@codesys/mcp-toolkit 2.7.0 → 2.8.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.
@@ -83,6 +83,55 @@ def op_debug_set(args):
83
83
  return {"id": key}
84
84
 
85
85
 
86
+ def op_debug_set_data(args):
87
+ """A data breakpoint: halt when a variable is written (optionally only when a condition holds)."""
88
+ import System
89
+ c = Ctx()
90
+ mcp_sim._online()
91
+ bpm = _bpm()
92
+ expr = args["expression"]
93
+ tasks = System.Array[str](args.get("tasks") or [])
94
+ try:
95
+ bp = bpm.CreateDataBreakpoint(c.app.guid, c.proj.handle, expr, int(args.get("size") or 0), tasks, _hit_always(), 0, False, args.get("condition") or "", "", "")
96
+ except AttributeError:
97
+ raise OpError("unavailable", "This CODESYS has no data breakpoints for scripts.")
98
+ bpm.SetBreakpoint(bp)
99
+ bpm.EnableBreakpoint(bp)
100
+ key = "data:%s" % expr
101
+ BPS[key] = bp
102
+ # With size 0 CODESYS keeps the breakpoint disabled; the server passes the variable's size.
103
+ return {"id": key, "enabled": bool(bp.IsEnabled)}
104
+
105
+
106
+ def _call_stack(c):
107
+ """The call stack while halted: each entry's POU and instance, innermost first."""
108
+ out = []
109
+ try:
110
+ from System.Reflection import BindingFlags
111
+ om = None
112
+ for tname in ("_3S.CoDeSys.MemoryTools.APEnvironment", "_3S.CoDeSys.VisualEditor.APEnvironment", "_3S.CoDeSys.RecipeManObject.APEnvironment"):
113
+ t = _find_type(tname)
114
+ p = t.GetProperty("OnlineMgr", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) if t is not None else None
115
+ if p is not None:
116
+ om = p.GetValue(None, None)
117
+ if om is not None:
118
+ break
119
+ if om is None:
120
+ return []
121
+ app = om.GetApplication(c.app.guid)
122
+ for e in list(app.CallStack or []):
123
+ # LocationInformation.EditorInstancePath: 'PLCWinNT.Application.PLC_PRG.fbCount';
124
+ # the task's own frames ('CallTask__MainTask', '__cycle__code__MainTask') are left out.
125
+ info = e.LocationInformation
126
+ path = to_text(info.EditorInstancePath or "")
127
+ if not path or path.split(".")[-1].startswith("__") or path.split(".")[-1].startswith("CallTask__"):
128
+ continue
129
+ out.append({"instance": path})
130
+ except Exception as ex:
131
+ out.append({"error": to_text(ex)[:160]})
132
+ return out
133
+
134
+
86
135
  def op_debug_clear(args):
87
136
  mcp_sim._online()
88
137
  bpm = _bpm()
@@ -120,6 +169,7 @@ def op_debug_status(args):
120
169
  out["at"]["implementation"] = pos["implementation"]
121
170
  except Exception as e:
122
171
  out["at_error"] = to_text(e)
172
+ out["call_stack"] = _call_stack(c)
123
173
  return out
124
174
 
125
175
 
@@ -135,14 +185,16 @@ def op_debug_step(args):
135
185
 
136
186
  def op_debug_continue(args):
137
187
  online = mcp_sim._online()
138
- online.start()
139
- mcp_sim._pause(200)
188
+ if to_text(online.application_state).lower() != "run":
189
+ online.start()
190
+ mcp_sim._pause(200)
140
191
  return {"state": to_text(online.application_state)}
141
192
 
142
193
 
143
194
  mcp_ops.OPS.update({
144
195
  "debug.positions": op_debug_positions,
145
196
  "debug.set": op_debug_set,
197
+ "debug.set_data": op_debug_set_data,
146
198
  "debug.clear": op_debug_clear,
147
199
  "debug.status": op_debug_status,
148
200
  "debug.step": op_debug_step,