--- a/.gitignore Thu Aug 22 12:16:45 2024 +0200
+++ b/.gitignore Sat Sep 07 12:50:57 2024 +0200
@@ -14,4 +14,6 @@
doc/locale/**
C_runtime/**/*.d
-C_runtime/**/*.o
\ No newline at end of file
+C_runtime/**/*.o
+beremiz_runtime
+**/tmp/**
--- a/C_runtime/PLCObject.cpp Thu Aug 22 12:16:45 2024 +0200
+++ b/C_runtime/PLCObject.cpp Sat Sep 07 12:50:57 2024 +0200
@@ -344,6 +344,9 @@
uint32_t PLCObject::LoadPLC(void)
{
+
+ // TODO use PLCLibMutex
+
// Load the last transferred PLC md5 hex digest
std::string md5sum;
try {
--- a/C_runtime/PLCObject.hpp Thu Aug 22 12:16:45 2024 +0200
+++ b/C_runtime/PLCObject.hpp Sat Sep 07 12:50:57 2024 +0200
@@ -71,7 +71,10 @@
uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken);
uint32_t StartPLC(void);
uint32_t StopPLC(bool * success);
+
+ // Public interface used by runtime
uint32_t AutoLoad();
+ uint32_t LogMessage(uint8_t level, std::string message);
private:
// A map of all the blobs
@@ -112,10 +115,9 @@
uint32_t BlobAsFile(const binary_t * BlobID, std::filesystem::path filename);
uint32_t LoadPLC(void);
uint32_t UnLoadPLC(void);
- uint32_t LogMessage(uint8_t level, std::string message);
uint32_t PurgePLC(void);
void PurgeTraceBuffer(void);
void TraceThreadProc(void);
};
-#endif
\ No newline at end of file
+#endif
--- a/CodeFileTreeNode.py Thu Aug 22 12:16:45 2024 +0200
+++ b/CodeFileTreeNode.py Sat Sep 07 12:50:57 2024 +0200
@@ -107,7 +107,7 @@
filepath = self.CodeFileName()
if os.path.isfile(filepath):
- xmlfile = open(filepath, 'r')
+ xmlfile = open(filepath, 'r', encoding='utf-8', errors='backslashreplace')
codefile_xml = xmlfile.read()
xmlfile.close()
--- a/IDEFrame.py Thu Aug 22 12:16:45 2024 +0200
+++ b/IDEFrame.py Sat Sep 07 12:50:57 2024 +0200
@@ -1116,7 +1116,7 @@
printout2 = GraphicPrintout(window, page_size, margins, True)
preview = wx.PrintPreview(printout, printout2, data)
- if preview.Ok():
+ if preview.IsOk():
preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)
preview_frame.Initialize()
@@ -2599,7 +2599,7 @@
self.PageSize = page_size
if self.PageSize[0] == 0 or self.PageSize[1] == 0:
self.PageSize = (1050, 1485)
- self.Preview = preview
+ self.IsPreview = lambda *_x : preview
self.Margins = margins
self.FontSize = 5
self.TextMargin = 3
@@ -2620,9 +2620,9 @@
def OnBeginDocument(self, startPage, endPage):
dc = self.GetDC()
- if not self.Preview and isinstance(dc, wx.PostScriptDC):
+ if not self.IsPreview() and isinstance(dc, wx.PostScriptDC):
dc.SetResolution(720)
- super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)
+ return super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)
def OnPrintPage(self, page):
dc = self.GetDC()
@@ -2630,21 +2630,21 @@
dc.Clear()
dc.SetUserScale(1.0, 1.0)
dc.SetDeviceOrigin(0, 0)
- dc.printing = not self.Preview
+ dc.printing = not self.IsPreview()
# Get the size of the DC in pixels
ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
pw, ph = self.GetPageSizePixels()
- dw, dh = dc.GetSizeTuple()
+ dw, dh = dc.GetSize().Get()
Xscale = (dw * ppiPrinterX) / (pw * 25.4)
Yscale = (dh * ppiPrinterY) / (ph * 25.4)
- fontsize = self.FontSize * Yscale
-
- margin_left = self.Margins[0].x * Xscale
- margin_top = self.Margins[0].y * Yscale
- area_width = dw - self.Margins[1].x * Xscale - margin_left
- area_height = dh - self.Margins[1].y * Yscale - margin_top
+ fontsize = round(self.FontSize * Yscale)
+
+ margin_left = round(self.Margins[0].x * Xscale)
+ margin_top = round(self.Margins[0].y * Yscale)
+ area_width = dw - round(self.Margins[1].x * Xscale) - margin_left
+ area_height = dh - round(self.Margins[1].y * Yscale) - margin_top
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
@@ -2667,7 +2667,7 @@
# Set the scale and origin
dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
- dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
+ dc.SetClippingRegion(posX, posY, round(self.PageSize[0] * scale), round(self.PageSize[1] * scale))
dc.SetUserScale(scale, scale)
self.Viewer.DoDrawing(dc, True)
--- a/LocalRuntimeMixin.py Thu Aug 22 12:16:45 2024 +0200
+++ b/LocalRuntimeMixin.py Sat Sep 07 12:50:57 2024 +0200
@@ -51,7 +51,10 @@
# shutdown local runtime
self.local_runtime.kill(gently=False)
# clear temp dir
- shutil.rmtree(self.local_runtime_tmpdir)
+ try:
+ shutil.rmtree(self.local_runtime_tmpdir)
+ except:
+ pass
self.local_runtime = None
--- a/PLCControler.py Thu Aug 22 12:16:45 2024 +0200
+++ b/PLCControler.py Sat Sep 07 12:50:57 2024 +0200
@@ -1373,7 +1373,8 @@
return False
def IsEndType(self, typename):
- if typename is not None:
+ # Check if the type is a base type
+ if type(typename) == str:
return not typename.startswith("ANY")
return True
--- a/ProjectController.py Thu Aug 22 12:16:45 2024 +0200
+++ b/ProjectController.py Sat Sep 07 12:50:57 2024 +0200
@@ -508,6 +508,9 @@
self._setBuildPath(BuildPath)
# get confnodes bloclist (is that usefull at project creation?)
self.RefreshConfNodesBlockLists()
+ # default scaling
+ for iec_lang in ["FBD", "LD", "SFC"]:
+ PLCControler.SetProjectProperties(self, properties={"scaling": {iec_lang: (8, 8)}})
# this will create files base XML files
self.SaveProject()
return None
--- a/bacnet/BacnetSlaveEditor.py Thu Aug 22 12:16:45 2024 +0200
+++ b/bacnet/BacnetSlaveEditor.py Sat Sep 07 12:50:57 2024 +0200
@@ -828,7 +828,7 @@
# use only to enable drag'n'drop
# self.VariablesGrid.SetDropTarget(VariableDropTarget(self))
self.VariablesGrid.Bind(
- wx.grid.EVT_GRID_CELL_CHANGING, self.OnVariablesGridCellChange)
+ wx.grid.EVT_GRID_CELL_CHANGED, self.OnVariablesGridCellChange)
# self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
# self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
self.MainSizer.Add(self.VariablesGrid, flag=wx.GROW)
--- a/controls/LocationCellEditor.py Thu Aug 22 12:16:45 2024 +0200
+++ b/controls/LocationCellEditor.py Sat Sep 07 12:50:57 2024 +0200
@@ -188,9 +188,7 @@
var_type = self.CellControl.GetVarType()
if var_type is not None:
self.Table.SetValueByName(row, 'Type', var_type)
- else:
- wx.CallAfter(self.Table.Parent.ShowErrorMessage,
- _("Selected location is identical to previous one"))
+
self.CellControl.Disable()
return changed
--- a/controls/SearchResultPanel.py Thu Aug 22 12:16:45 2024 +0200
+++ b/controls/SearchResultPanel.py Sat Sep 07 12:50:57 2024 +0200
@@ -172,7 +172,7 @@
"type": ITEM_PROJECT,
"data": None,
"text": None,
- "matches": None,
+ "matches": 0,
}
search_results_tree_children = search_results_tree_infos.setdefault("children", [])
for tagname in self.ElementsOrder:
@@ -291,7 +291,7 @@
start_idx = start[1]
end_idx = reduce(lambda x, y: x + y, [len(x) + 1 for x in text_lines[:end[0] - start[0]]], end[1] + 1)
style = wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247))
- elif infos["type"] is not None and infos["matches"] > 1:
+ elif infos["type"] is not None and infos["matches"] > 0:
text = _("(%d matches)") % infos["matches"]
start_idx, end_idx = 0, len(text)
style = wx.TextAttr(wx.Colour(0, 127, 174))
--- a/dialogs/ActionBlockDialog.py Thu Aug 22 12:16:45 2024 +0200
+++ b/dialogs/ActionBlockDialog.py Sat Sep 07 12:50:57 2024 +0200
@@ -148,7 +148,7 @@
self.ActionsGrid = CustomGrid(self, size=wx.Size(-1, 250), style=wx.VSCROLL)
self.ActionsGrid.DisableDragGridSize()
self.ActionsGrid.EnableScrolling(False, True)
- self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING,
+ self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED,
self.OnActionsGridCellChange)
main_sizer.Add(self.ActionsGrid, border=20,
flag=wx.GROW | wx.LEFT | wx.RIGHT)
--- a/dialogs/FBDVariableDialog.py Thu Aug 22 12:16:45 2024 +0200
+++ b/dialogs/FBDVariableDialog.py Sat Sep 07 12:50:57 2024 +0200
@@ -155,10 +155,8 @@
# Get variable expression and select corresponding value in name list
# box if it exists
selected = self.Expression.GetValue()
- if selected != "" and self.VariableName.FindString(selected) != wx.NOT_FOUND:
- self.VariableName.SetStringSelection(selected)
- else:
- self.VariableName.SetSelection(wx.NOT_FOUND)
+ self.VariableName.SetSelection(
+ wx.NOT_FOUND if selected == "" else self.VariableName.FindString(selected, True))
# Disable name list box if no name present inside
self.VariableName.Enable(self.VariableName.GetCount() > 0)
@@ -185,10 +183,7 @@
# Set expression text control value
self.Expression.ChangeValue(value)
# Select corresponding text in name list box if it exists
- if self.VariableName.FindString(value) != wx.NOT_FOUND:
- self.VariableName.SetStringSelection(value)
- else:
- self.VariableName.SetSelection(wx.NOT_FOUND)
+ self.VariableName.SetSelection(self.VariableName.FindString(value, True))
# Parameter is variable execution order
elif name == "executionOrder":
@@ -265,7 +260,7 @@
"""
# Select the corresponding value in name list box if it exists
self.VariableName.SetSelection(
- self.VariableName.FindString(self.Expression.GetValue()))
+ self.VariableName.FindString(self.Expression.GetValue(), True))
self.Refresh()
event.Skip()
--- a/editors/TextViewer.py Thu Aug 22 12:16:45 2024 +0200
+++ b/editors/TextViewer.py Sat Sep 07 12:50:57 2024 +0200
@@ -877,13 +877,15 @@
lineText = self.Editor.GetTextRange(start_pos, end_pos).replace("\t", " ")
# Code completion
- if key == wx.WXK_SPACE and event.ControlDown():
+ if key == wx.WXK_SPACE and event.RawControlDown():
words = lineText.split(" ")
words = [word for i, word in enumerate(words) if word != '' or i == len(words) - 1]
kw = []
+ self.RefreshVariableTree()
+
if self.TextSyntax == "IL":
if len(words) == 1:
kw = self.Keywords
@@ -898,13 +900,17 @@
kw = self.Keywords + list(self.Variables.keys()) + list(self.Functions.keys())
if len(kw) > 0:
if len(words[-1]) > 0:
- kw = [keyword for keyword in kw if keyword.startswith(words[-1])]
+ kw = [keyword for keyword in kw if keyword.startswith(words[-1].upper())]
+ if len(kw) > 0:
kw.sort()
self.Editor.AutoCompSetIgnoreCase(True)
self.Editor.AutoCompShow(len(words[-1]), " ".join(kw))
key_handled = True
elif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER:
- if self.TextSyntax in ["ST", "ALL"]:
+ if self.Editor.AutoCompActive():
+ self.Editor.AutoCompComplete()
+ key_handled = True
+ elif self.TextSyntax in ["ST", "ALL"]:
indent = self.Editor.GetLineIndentation(line)
if LineStartswith(lineText.strip(), self.BlockStartKeywords):
indent = (indent // 2 + 1) * 2
--- a/editors/Viewer.py Thu Aug 22 12:16:45 2024 +0200
+++ b/editors/Viewer.py Sat Sep 07 12:50:57 2024 +0200
@@ -1542,15 +1542,18 @@
# Popup menu functions
# -------------------------------------------------------------------------------
- def GetForceVariableMenuFunction(self, iec_path, element):
- iec_type = self.GetDataType(iec_path)
-
- def ForceVariableFunction(event):
- if iec_type is not None:
- dialog = ForceVariableDialog(self.ParentWindow, iec_type, str(element.GetValue()))
- if dialog.ShowModal() == wx.ID_OK:
- self.ParentWindow.AddDebugVariable(iec_path)
- self.ForceDataValue(iec_path, dialog.GetValue())
+ def GetForceVariableMenuFunction(self, iec_path, iec_type, value, immediate = False):
+
+ def ForceVariableFunction(event, value=value):
+ if not immediate:
+ # use value as default value in dialog
+ dialog = ForceVariableDialog(self.ParentWindow, iec_type, str(value))
+ if dialog.ShowModal() != wx.ID_OK:
+ return
+ value = dialog.GetValue()
+ self.ParentWindow.AddDebugVariable(iec_path)
+ self.ForceDataValue(iec_path, value)
+
return ForceVariableFunction
def GetReleaseVariableMenuFunction(self, iec_path):
@@ -1570,13 +1573,39 @@
def PopupForceMenu(self):
iec_path = self.GetElementIECPath(self.SelectedElement)
+
+ if iec_path is None:
+ # GetElementIECPath() does not work for variables and coils
+ # In such case get the IEC path using the instance path
+ for ElementType in [FBD_Variable, LD_Coil]:
+ if isinstance(self.SelectedElement, ElementType):
+ instance_path = self.GetInstancePath(True)
+ iec_path = "%s.%s" % (instance_path, self.SelectedElement.GetName())
+ menu = wx.Menu(title='')
+ break
+
if iec_path is not None:
menu = wx.Menu(title='')
- item = self.AppendItem(menu,
- _("Force value"),
- self.GetForceVariableMenuFunction(
- iec_path.upper(),
- self.SelectedElement))
+ iec_type = self.GetDataType(iec_path)
+ if iec_type == "BOOL":
+ self.AppendItem(menu,
+ _("Force Toggle"),
+ self.GetForceVariableMenuFunction(
+ iec_path.upper(), iec_type, not(self.SelectedElement.GetValue()), True))
+ self.AppendItem(menu,
+ _("Force True"),
+ self.GetForceVariableMenuFunction(
+ iec_path.upper(), iec_type, True, True))
+ self.AppendItem(menu,
+ _("Force False"),
+ self.GetForceVariableMenuFunction(
+ iec_path.upper(), iec_type, False, True))
+ else:
+ self.AppendItem(menu,
+ _("Force value"),
+ self.GetForceVariableMenuFunction(
+ iec_path.upper(), iec_type,
+ self.SelectedElement.GetValue()))
ritem = self.AppendItem(menu,
_("Release value"),
@@ -1585,6 +1614,7 @@
ritem.Enable(True)
else:
ritem.Enable(False)
+
if self.Editor.HasCapture():
self.Editor.ReleaseMouse()
self.Editor.PopupMenu(menu)
--- a/etherlab/ConfigEditor.py Thu Aug 22 12:16:45 2024 +0200
+++ b/etherlab/ConfigEditor.py Sat Sep 07 12:50:57 2024 +0200
@@ -672,7 +672,7 @@
self.ProcessVariablesGrid = CustomGrid(self.EthercatMasterEditor, style=wx.VSCROLL)
self.ProcessVariablesGrid.SetMinSize(wx.Size(0, 150))
self.ProcessVariablesGrid.SetDropTarget(ProcessVariableDropTarget(self))
- self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING,
+ self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED,
self.OnProcessVariablesGridCellChange)
self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,
self.OnProcessVariablesGridCellLeftClick)
@@ -697,7 +697,7 @@
self.StartupCommandsGrid = CustomGrid(self.EthercatMasterEditor, style=wx.VSCROLL)
self.StartupCommandsGrid.SetDropTarget(StartupCommandDropTarget(self))
self.StartupCommandsGrid.SetMinSize(wx.Size(0, 150))
- self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING,
+ self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED,
self.OnStartupCommandsGridCellChange)
self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN,
self.OnStartupCommandsGridEditorShow)
--- a/graphics/GraphicCommons.py Thu Aug 22 12:16:45 2024 +0200
+++ b/graphics/GraphicCommons.py Sat Sep 07 12:50:57 2024 +0200
@@ -349,10 +349,10 @@
posx, posy = self.GetPosition()
min_width, min_height = self.GetMinSize()
if width < min_width:
- self.Pos.x = max(0, self.Pos.x - (width - min_width) * x_factor)
+ self.Pos.x = max(0, round(self.Pos.x - (width - min_width) * x_factor))
width = min_width
if height < min_height:
- self.Pos.y = max(0, self.Pos.y - (height - min_height) * y_factor)
+ self.Pos.y = max(0, round(self.Pos.y - (height - min_height) * y_factor))
height = min_height
if scaling is not None:
self.Pos.x = round_scaling(self.Pos.x, scaling[0])
@@ -1026,16 +1026,16 @@
"""
# Create a new connector
- def __init__(self, parent, name, type, position, direction, negated=False, edge="none", onlyone=False):
+ def __init__(self, parent, name, Type, position, direction, negated=False, edge="none", onlyone=False):
DebugDataConsumer.__init__(self)
ToolTipProducer.__init__(self, parent.Parent)
self.ParentBlock = parent
self.Name = name
- self.Type = type
+ self.Type = Type
self.Pos = position
self.Direction = direction
self.Wires = []
- if self.ParentBlock.IsOfType("BOOL", type):
+ if self.ParentBlock.IsOfType("BOOL", Type):
self.Negated = negated
self.Edge = edge
else:
@@ -1141,8 +1141,8 @@
return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type)
# Changes the connector name
- def SetType(self, type):
- self.Type = type
+ def SetType(self, Type):
+ self.Type = Type
for wire, _handle in self.Wires:
wire.SetValid(wire.IsConnectedCompatible())
--- a/graphics/LD_Objects.py Thu Aug 22 12:16:45 2024 +0200
+++ b/graphics/LD_Objects.py Sat Sep 07 12:50:57 2024 +0200
@@ -593,11 +593,11 @@
dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
dc.SetLogicalFunction(wx.AND)
# Draw two rectangles for representing the contact
- left_left = (self.Pos.x - 1) * scalex - 2
- right_left = (self.Pos.x + self.Size[0] - 2) * scalex - 2
- top = (self.Pos.y - 1) * scaley - 2
- width = 4 * scalex + 5
- height = (self.Size[1] + 3) * scaley + 5
+ left_left = round((self.Pos.x - 1) * scalex) - 2
+ right_left = round((self.Pos.x + self.Size[0] - 2) * scalex) - 2
+ top = round((self.Pos.y - 1) * scaley) - 2
+ width = round(4 * scalex + 5)
+ height = round((self.Size[1] + 3) * scaley) + 5
dc.DrawRectangle(left_left, top, width, height)
dc.DrawRectangle(right_left, top, width, height)
@@ -974,31 +974,24 @@
elif self.Type == COIL_FALLING:
typetext = "N"
- if getattr(dc, "printing", False) and not isinstance(dc, wx.PostScriptDC):
- # Draw an clipped ellipse for representing the coil
- clipping_box = dc.GetClippingBox()
- dc.SetClippingRegion(self.Pos.x - 1, self.Pos.y, self.Size[0] + 2, self.Size[1] + 1)
- dc.DrawEllipse(self.Pos.x, self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1, self.Size[0], int(self.Size[1] * sqrt(2)) - 1)
- dc.DestroyClippingRegion()
- if clipping_box != (0, 0, 0, 0):
- dc.SetClippingRegion(*clipping_box)
- name_size = dc.GetTextExtent(self.Name)
- if typetext != "":
- type_size = dc.GetTextExtent(typetext)
- else:
- # Draw a two ellipse arcs for representing the coil
- dc.DrawEllipticArc(self.Pos.x, self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1, self.Size[0], int(self.Size[1] * sqrt(2)) - 1, 135, 225)
- dc.DrawEllipticArc(self.Pos.x, self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1, self.Size[0], int(self.Size[1] * sqrt(2)) - 1, -45, 45)
- # Draw a point to avoid hole in left arc
- if not getattr(dc, "printing", False):
- if self.Value is not None and self.Value:
- dc.SetPen(MiterPen(wx.GREEN))
- else:
- dc.SetPen(MiterPen(wx.BLACK))
- dc.DrawPoint(self.Pos.x + 1, self.Pos.y + self.Size[1] // 2 + 1)
- name_size = self.NameSize
- if typetext != "":
- type_size = self.TypeSize
+ printing = getattr(dc, "printing", False)
+ # Draw a two ellipse arcs for representing the coil
+ pos = (self.Pos.x,
+ self.Pos.y - round(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1,
+ self.Size[0], round(self.Size[1] * sqrt(2)) - 1)
+
+ if printing:
+ # workaround for printing bug with DrawEllipticArc
+ # add an offset to the y position proportional to the height of the ellipse
+ # sqrt(2) ratio obtained heuristically
+ pos = (pos[0], pos[1] + round(sqrt(2)*pos[3]), pos[2], pos[3])
+
+ dc.DrawEllipticArc(*pos, 135, 225)
+ dc.DrawEllipticArc(*pos, -45, 45)
+
+ name_size = self.NameSize
+ if typetext != "":
+ type_size = self.TypeSize
# Draw coil name
name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) // 2,
--- a/plcopen/structures.py Thu Aug 22 12:16:45 2024 +0200
+++ b/plcopen/structures.py Sat Sep 07 12:50:57 2024 +0200
@@ -340,7 +340,7 @@
ST_BLOCK_START_KEYWORDS = ["IF", "ELSIF", "ELSE", "CASE", "FOR", "WHILE", "REPEAT"]
ST_BLOCK_END_KEYWORDS = ["END_IF", "END_CASE", "END_FOR", "END_WHILE", "END_REPEAT"]
ST_KEYWORDS = [
- "TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT",
+ "TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", "CONTINUE",
"RETURN", "NOT", "MOD", "AND", "XOR", "OR"
] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS
--- a/requirements.txt Thu Aug 22 12:16:45 2024 +0200
+++ b/requirements.txt Sat Sep 07 12:50:57 2024 +0200
@@ -24,7 +24,7 @@
lxml==4.9.2
matplotlib==3.7.1
msgpack==1.0.5
-Nevow @ git+https://git@github.com/beremiz/nevow-py3.git@6deba7284e159af46a412699f046e060d7026cf9
+Nevow @ git+https://git@github.com/beremiz/nevow-py3.git@81c2eaeaaa20022540a98a3106f72e0199fbcc1b
numpy==1.24.3
packaging==23.1
Pillow==9.5.0
--- a/util/ProcessLogger.py Thu Aug 22 12:16:45 2024 +0200
+++ b/util/ProcessLogger.py Sat Sep 07 12:50:57 2024 +0200
@@ -136,7 +136,7 @@
if _debug and self.logger:
self.logger.write("(DEBUG) launching:\n" + self.Command_str + "\n")
- self.Proc = subprocess.Popen(self.Command, encoding="utf-8", **popenargs)
+ self.Proc = subprocess.Popen(self.Command, encoding="utf-8", errors="backslashreplace", **popenargs)
self.outt = outputThread(
self.Proc,