--- a/ConfigTreeNode.py Fri Dec 25 17:12:02 2020 +0000
+++ b/ConfigTreeNode.py Mon Jan 18 10:59:28 2021 +0100
@@ -46,6 +46,7 @@
from xmlclass import GenerateParserFromXSDstring
from PLCControler import LOCATION_CONFNODE
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
+from POULibrary import UserAddressedException _BaseParamsParser = GenerateParserFromXSDstring("""<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
@@ -469,21 +470,21 @@
def GetContextualMenuItems(self):
- if self._View is None and self.EditorType is not None:
+ def GetView(self, onlyopened=False): + if self._View is None and not onlyopened and self.EditorType is not None: app_frame = self.GetCTRoot().AppFrame
self._View = self.EditorType(app_frame.TabsOpened, self, app_frame)
def _OpenView(self, name=None, onlyopened=False):
+ view = self.GetView(onlyopened) name = self.CTNFullName()
app_frame = self.GetCTRoot().AppFrame
- app_frame.EditProjectElement(view, name)
+ app_frame.EditProjectElement(view, name, onlyopened) --- a/ProjectController.py Fri Dec 25 17:12:02 2020 +0000
+++ b/ProjectController.py Mon Jan 18 10:59:28 2021 +0100
@@ -159,6 +159,7 @@
+ self.logger.write_error(_("Couldn't launch IEC compiler to determine compatible options.\n")) @@ -254,7 +255,7 @@
# Setup debug information
+ self.DebugUpdatePending = False self.ResetIECProgramsAndVariables()
# In both new or load scenario, no need to save
@@ -276,8 +277,6 @@
self.debug_status = PlcStatus.Stopped
- self.DebugTimer.cancel()
@@ -1538,7 +1537,6 @@
return debug_status, ticks, buffers
def RegisterDebugVarToConnector(self):
@@ -1577,25 +1575,15 @@
self._connector.SetTraceVariablesList([])
self.debug_status, _debug_ticks, _buffers = self.SnapshotAndResetDebugValuesBuffers()
+ self.DebugUpdatePending = False return self.previous_plcstate == PlcStatus.Started
- def ReArmDebugRegisterTimer(self):
- if self.DebugTimer is not None:
- self.DebugTimer.cancel()
- # Prevent to call RegisterDebugVarToConnector when PLC is not started
- # If an output location var is forced it's leads to segmentation fault in runtime
- # Links between PLC located variables and real variables are not ready
- if self.IsPLCStarted():
- # Timer to prevent rapid-fire when registering many variables
- # use wx.CallAfter use keep using same thread. TODO : use wx.Timer
- self.DebugTimer = Timer(
- 0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector])
- # Rearm anti-rapid-fire timer
- self.DebugTimer.start()
+ def AppendDebugUpdate(self): + if not self.DebugUpdatePending : + wx.CallAfter(self.RegisterDebugVarToConnector) + self.DebugUpdatePending = True def GetDebugIECVariableType(self, IECPath):
_Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
@@ -1625,7 +1613,7 @@
IECdebug_data[0][callableobj] = buffer_list
- self.ReArmDebugRegisterTimer()
+ self.AppendDebugUpdate() @@ -1641,12 +1629,12 @@
IECdebug_data[0].itervalues(),
- self.ReArmDebugRegisterTimer()
+ self.AppendDebugUpdate() def UnsubscribeAllDebugIECVariable(self):
- self.ReArmDebugRegisterTimer()
+ self.AppendDebugUpdate() def ForceDebugIECVariable(self, IECPath, fvalue):
if IECPath not in self.IECdebug_datas:
@@ -1657,7 +1645,7 @@
IECdebug_data[2] = "Forced"
IECdebug_data[3] = fvalue
- self.ReArmDebugRegisterTimer()
+ self.AppendDebugUpdate() def ReleaseDebugIECVariable(self, IECPath):
if IECPath not in self.IECdebug_datas:
@@ -1668,7 +1656,7 @@
IECdebug_data[2] = "Registered"
- self.ReArmDebugRegisterTimer()
+ self.AppendDebugUpdate() def CallWeakcallables(self, IECPath, function_name, *cargs):
data_tuple = self.IECdebug_datas.get(IECPath, None)
--- a/bacnet/bacnet.py Fri Dec 25 17:12:02 2020 +0000
+++ b/bacnet/bacnet.py Mon Jan 18 10:59:28 2021 +0100
@@ -790,6 +790,6 @@
return ([(Generated_BACnet_c_mainfile_name, CFLAGS)], LDFLAGS, True,
- ("runtime_bacnet_websettings_%s.py" % location_str, open(runtimefile_path, "rb")),
+ ("runtime_%s_bacnet_websettings.py" % location_str, open(runtimefile_path, "rb")), #return [(Generated_BACnet_c_mainfile_name, CFLAGS)], LDFLAGS, True, ('extrafile1.txt', extra_file_handle)
--- a/bacnet/web_settings.py Fri Dec 25 17:12:02 2020 +0000
+++ b/bacnet/web_settings.py Mon Jan 18 10:59:28 2021 +0100
@@ -295,7 +295,7 @@
# location_str is replaced by extension's value in CTNGenerateC call
-def _runtime_bacnet_websettings_%(location_str)s_init():
+def _runtime_%(location_str)s_bacnet_websettings_init(): # Callback function, called (by PLCObject.py) when a new PLC program
# (i.e. XXX.so file) is transfered to the PLC runtime
@@ -383,7 +383,7 @@
# location_str is replaced by extension's value in CTNGenerateC call
-def _runtime_bacnet_websettings_%(location_str)s_cleanup():
+def _runtime_%(location_str)s_bacnet_websettings_cleanup(): # Callback function, called (by PLCObject.py) when a PLC program is unloaded from memory
--- a/controls/LogViewer.py Fri Dec 25 17:12:02 2020 +0000
+++ b/controls/LogViewer.py Mon Jan 18 10:59:28 2021 +0100
@@ -339,7 +339,7 @@
if wx.Platform == '__WXMSW__':
self.Font = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='Courier New')
- self.Font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='Courier')
+ self.Font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName='FreeMono') self.MessagePanel.Bind(wx.EVT_LEFT_UP, self.OnMessagePanelLeftUp)
self.MessagePanel.Bind(wx.EVT_RIGHT_UP, self.OnMessagePanelRightUp)
self.MessagePanel.Bind(wx.EVT_LEFT_DCLICK, self.OnMessagePanelLeftDCLick)
--- a/docutil/docsvg.py Fri Dec 25 17:12:02 2020 +0000
+++ b/docutil/docsvg.py Mon Jan 18 10:59:28 2021 +0100
@@ -34,16 +34,23 @@
if wx.Platform == '__WXMSW__':
from six.moves import winreg
- svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
- 'Software\\Classes\\svgfile\\shell\\Inkscape\\command')
+ svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, + 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') - svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
- 'Software\\Classes\\inkscape.svg\\shell\\open\\command')
+ svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, + 'Software\\Classes\\inkscape.svg\\shell\\open\\command') svgexepath = svgexepath.replace('"%1"', '').strip()
return svgexepath.replace('"', '')
- return os.path.join("/usr/bin", "inkscape")
+ # TODO: search for inkscape in $PATH + svgexepath = os.path.join("/usr/bin", "inkscape") + if os.path.exists(svgexepath): def open_win_svg(svgexepath, svgfile):
--- a/etherlab/EthercatMaster.py Fri Dec 25 17:12:02 2020 +0000
+++ b/etherlab/EthercatMaster.py Mon Jan 18 10:59:28 2021 +0100
@@ -97,6 +97,8 @@
return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "",
("runtime_etherlab.py", open(GetLocalPath("runtime_etherlab.py"))))
+ # TODO : rename to match runtime_{location}_extname.py format # --------------------------------------------------
# --------------------------------------------------
--- a/etherlab/runtime_etherlab.py Fri Dec 25 17:12:02 2020 +0000
+++ b/etherlab/runtime_etherlab.py Mon Jan 18 10:59:28 2021 +0100
@@ -114,6 +114,7 @@
+# TODO : rename to match _runtime_{location}_extname_init() format def _runtime_etherlab_init():
global KMSGPollThread, StopKMSGThread
@@ -121,6 +122,7 @@
+# TODO : rename to match _runtime_{location}_extname_cleanup() format def _runtime_etherlab_cleanup():
global KMSGPollThread, StopKMSGThread, SDOThread
--- a/modbus/web_settings.py Fri Dec 25 17:12:02 2020 +0000
+++ b/modbus/web_settings.py Mon Jan 18 10:59:28 2021 +0100
@@ -183,7 +183,7 @@
("baud" , _("Baud Rate") , ctypes.c_int, MB_Baud ),
("parity" , _("Parity") , ctypes.c_int, MB_Parity ),
("stop_bits" , _("Stop Bits") , ctypes.c_int, MB_StopBits ),
- ("slave_id" , _("Slave ID") , ctypes.c_ulonglong, annotate.Integer)
+ ("slave_id" , _("Slave ID") , ctypes.c_ubyte, annotate.Integer) @@ -228,7 +228,7 @@
# This allows us to confirm the saved data contains the correct addr_type
- save_info["addr_type"] = ["addr_type"]
+ save_info["addr_type"] = WebNode_entry["addr_type"] save_info["node_type"] = WebNode_entry["node_type"]
save_info["config" ] = newConfig
@@ -262,7 +262,7 @@
filename = _WebNodeList[WebNode_id]["filename"]
#if os.path.isfile(filename):
- save_info = json.load(open(filename))
+ save_info = json.load(open(os.path.realpath(filename))) @@ -526,7 +526,7 @@
-def _runtime_modbus_websettings_%(location_str)s_init():
+def _runtime_%(location_str)s_modbus_websettings_init(): Callback function, called (by PLCObject.py) when a new PLC program
(i.e. XXX.so file) is transfered to the PLC runtime
@@ -609,7 +609,7 @@
-def _runtime_modbus_websettings_%(location_str)s_cleanup():
+def _runtime_%(location_str)s_modbus_websettings_cleanup(): Callback function, called (by PLCObject.py) when a PLC program is unloaded from memory
--- a/py_ext/PythonFileCTNMixin.py Fri Dec 25 17:12:02 2020 +0000
+++ b/py_ext/PythonFileCTNMixin.py Mon Jan 18 10:59:28 2021 +0100
@@ -36,6 +36,8 @@
from py_ext.PythonEditor import PythonEditor
class PythonFileCTNMixin(CodeFile):
@@ -95,19 +97,41 @@
getattr(self.CodeFile, section).getanyText() + "\n" + \
self.PostSectionsTexts.get(section, "")
+ def CTNGlobalInstances(self): + variables = self.CodeFileVariables(self.CodeFile) + ret = [(variable.getname(), + for variable in variables] + location_str = "_".join(map(str, self.GetCurrentLocation())) + ret.append(("On_"+location_str+"_Change", "python_poll", "")) + def GetVarOnChangeContent(var): + returns given variable onchange field + function is meant to allow customization + return var.getonchange() def CTNGenerate_C(self, buildpath, locations):
# location string for that CTN
location_str = "_".join(map(str, self.GetCurrentLocation()))
configname = self.GetCTRoot().GetProjectConfigNames()[0]
- return '"' + var.getonchange() + \
- "('" + var.getname() + "')\"" \
- if var.getonchange() else '""'
+ for onchangecall in self.GetVarOnChangeContent(var).split(','): + onchangecall = onchangecall.strip() + result.append(onchangecall + "('" + var.getname() + "')") - return repr(var.getonchange()) \
- if var.getonchange() else None
+ content = self.GetVarOnChangeContent(var) + return repr(content) if content else None pyextname = self.CTNName()
@@ -124,6 +148,9 @@
self.CodeFile.variables.variable)
+ onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]]) # python side PLC global variables access stub
globalstubs = "\n".join([
@@ -142,8 +169,21 @@
-""" % varinfo for varinfo in varinfos])
+_PyOnChangeCount_%(name)s = ctypes.c_uint.in_dll(PLCBinary,"__%(name)s_onchange_count") +_PyOnChangeFirst_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_firstval") +_PyOnChangeLast_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_lastval") +""" % varinfo if varinfo["onchange"] else "") for varinfo in varinfos]) + on_change_func_body = "\n".join([""" + if _PyOnChangeCount_%(name)s.value > 0: + try:""" % varinfo + """ + """.join(varinfo['onchangecode'])+""" + errors.append("%(name)s: "+str(e)) +""" % varinfo for varinfo in varinfos if varinfo["onchange"]]) # Runtime calls (start, stop, init, and cleanup)
for section in self.SECTIONS_NAMES:
@@ -163,6 +203,9 @@
"globalstubs": globalstubs,
"globalsection": globalsection,
+ "location_str": location_str, + "on_change_func_body":on_change_func_body, + "onchange_var_count": onchange_var_count @@ -174,6 +217,11 @@
## Code for PLC global variable access
from runtime.typemapping import TypeTranslator
+_PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s +_PySafeGetChanges_%(pyextname)s.restype = None +_PySafeGetChanges_%(pyextname)s.argtypes = None _%(pyextname)sGlobalsDesc = []
__ext_name__ = "%(pyextname)s"
PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
@@ -185,6 +233,13 @@
## Beremiz python runtime calls
+def On_%(pyextname)s_Change(): + _PySafeGetChanges_%(pyextname)s() + raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors)) @@ -220,7 +275,12 @@
-PYTHON_POLL* __%(name)s_notifier;
+unsigned int __%(name)s_rbuffer_written = 0; +IEC_%(IECtype)s __%(name)s_rbuffer_firstval; +IEC_%(IECtype)s __%(name)s_rbuffer_lastval; +unsigned int __%(name)s_onchange_count = 0; +IEC_%(IECtype)s __%(name)s_onchange_firstval; +IEC_%(IECtype)s __%(name)s_onchange_lastval; @@ -243,16 +303,27 @@
if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
+ if(__%(name)s_rbuffer_written == 0); + __%(name)s_rbuffer_firstval = __%(name)s_rbuffer; + __%(name)s_rbuffer_lastval = tmp; __%(name)s_rbuffer = tmp;
- PYTHON_POLL_body__(__%(name)s_notifier);
+ /* count one more change */ + __%(name)s_rbuffer_written += 1; AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
- varinitonchangefmt = """\
- __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
- __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
- __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
+ varcollectchangefmt = """\ + while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1)); + __%(name)s_onchange_count = __%(name)s_rbuffer_written; + __%(name)s_onchange_firstval = __%(name)s_rbuffer_firstval; + __%(name)s_onchange_lastval = __%(name)s_rbuffer_lastval; + /* mark variable as unchanged */ + __%(name)s_rbuffer_written = 0; + AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); vardec = "\n".join([(vardecfmt + vardeconchangefmt
if varinfo["onchange"] else vardecfmt) % varinfo
@@ -261,16 +332,20 @@
varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
for varinfo in varinfos])
- varinit = "\n".join([varinitonchangefmt %
- dict(onchangelen=len(varinfo["onchangecode"]), **varinfo)
+ varcollectchange = "\n".join([varcollectchangefmt % varinfo for varinfo in varinfos if varinfo["onchange"]])
+ pysafe_pypoll_code = "On_"+pyextname+"_Change()"
"location_str": location_str,
+ "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"', + "pysafe_pypoll_code_len": len(pysafe_pypoll_code), + "varcollectchange": varcollectchange, + "onchange_var_count": onchange_var_count # TODO : use config name obtained from model instead of default
@@ -286,12 +361,17 @@
+PYTHON_POLL* __%(location_str)s_notifier; /* User variables reference */
/* Beremiz confnode functions */
int __init_%(location_str)s(int argc,char **argv){
+ __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE(); + __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); + __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s)); @@ -302,9 +382,22 @@
+static int passing_changes_to_python = 0; void __publish_%(location_str)s(void){
+ int some_change_found = 0; + passing_changes_to_python |= some_change_found; + // call python part if there was at least a change + if(passing_changes_to_python){ + PYTHON_POLL_body__(__%(location_str)s_notifier); + passing_changes_to_python &= !(__GET_VAR(__%(location_str)s_notifier->ACK,)); +void* PySafeGetChanges_%(location_str)s(void){ Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
--- a/runtime/NevowServer.py Fri Dec 25 17:12:02 2020 +0000
+++ b/runtime/NevowServer.py Mon Jan 18 10:59:28 2021 +0100
@@ -39,6 +39,7 @@
import util.paths as paths
from runtime.loglevels import LogLevels, LogLevelsDict
+from runtime import MainWorker, GetPLCObjectSingleton PAGE_TITLE = 'Beremiz Runtime Web Interface'
@@ -217,6 +218,18 @@
"Send a message to the log"),
+ # pylint: disable=no-self-argument + def restartOrRepairPLC( + ctx=annotate.Context(), + action=annotate.Choice(["Restart", "Repair"], + restartOrRepairPLC = annotate.autocallable(restartOrRepairPLC, @@ -284,9 +297,16 @@
def sendLogMessage(self, level, message, **kwargs):
level = LogLevelsDict[level]
- if _PySrv.plcobj is not None:
- _PySrv.plcobj.LogMessage(
- level, "Web form log message: " + message)
+ GetPLCObjectSingleton().LogMessage( + level, "Web form log message: " + message) + def restartOrRepairPLC(self, action, **kwargs): + if(action == "Repair"): + GetPLCObjectSingleton().RepairPLC() def locateChild(self, ctx, segments):
if segments[0] in customSettingsURLs:
--- a/runtime/PLCObject.py Fri Dec 25 17:12:02 2020 +0000
+++ b/runtime/PLCObject.py Mon Jan 18 10:59:28 2021 +0100
@@ -360,8 +360,18 @@
v = parent.python_runtime_vars["_"+name+"_pack"](t, value)
parent.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
+ class OnChangeStateClass(object): + def __getattr__(self, name): + u = parent.python_runtime_vars["_"+name+"_unpack"] + return type("changedesc",(),dict( + count = parent.python_runtime_vars["_PyOnChangeCount_"+name].value, + first = u(parent.python_runtime_vars["_PyOnChangeFirst_"+name]), + last = u(parent.python_runtime_vars["_PyOnChangeLast_"+name]))) self.python_runtime_vars.update({
"PLCGlobals": PLCSafeGlobals(),
+ "OnChange": OnChangeStateClass(), "WorkingDir": self.workingdir,
"PLCBinary": self.PLClibraryHandle,
@@ -440,6 +450,7 @@
self.PythonRuntimeCall("start")
self.PythonRuntimeCall("stop", reverse_order=True)
@@ -471,8 +482,6 @@
self._fail(_("Problem starting PLC : can't load PLC"))
if self.CurrentPLCFilename is not None and self.PLCStatus == PlcStatus.Stopped:
c_argv = ctypes.c_char_p * len(self.argv)
res = self._startPLC(len(self.argv), c_argv(*self.argv))
@@ -546,12 +555,15 @@
- def _BlobAsFile(self, blobID, newpath):
+ def BlobAsFile(self, blobID, newpath): blob = self.blobs.pop(blobID, None)
raise Exception(_("Missing data to create file: {}").format(newpath))
+ self._BlobAsFile(blob, newpath) + def _BlobAsFile(self, blob, newpath): @@ -610,13 +622,13 @@
- self._BlobAsFile(plc_object, new_PLC_filename)
+ self.BlobAsFile(plc_object, new_PLC_filename) log = open(extra_files_log, "w")
for fname, blobID in extrafiles:
fpath = os.path.join(self.workingdir, fname)
- self._BlobAsFile(blobID, fpath)
+ self.BlobAsFile(blobID, fpath) # Store new PLC filename based on md5 key
--- a/targets/plc_debug.c Fri Dec 25 17:12:02 2020 +0000
+++ b/targets/plc_debug.c Mon Jan 18 10:59:28 2021 +0100
@@ -100,7 +100,7 @@
/* init local static vars */
-#ifndef TARGET_ONLINE_DEBUG_DISABLE
+#ifndef TARGET_ONLINE_DEBUG_DISABLE buffer_cursor = debug_buffer;
buffer_state = BUFFER_FREE;
@@ -109,9 +109,9 @@
/* Iterate over all variables to fill debug buffer */
- __for_each_variable_do(RemindIterator);
+ __for_each_variable_do(RemindIterator); - char mstr[] = "RETAIN memory invalid - defaults used";
+ char mstr[] = "RETAIN memory invalid - defaults used"; LogMessage(LOG_WARNING, mstr, sizeof(mstr));
@@ -124,7 +124,7 @@
void __cleanup_debug(void)
-#ifndef TARGET_ONLINE_DEBUG_DISABLE
+#ifndef TARGET_ONLINE_DEBUG_DISABLE buffer_cursor = debug_buffer;
@@ -150,16 +150,14 @@
if(flags & ( __IEC_DEBUG_FLAG | __IEC_RETAIN_FLAG)){
USINT size = __get_type_enum_size(dsc->type);
-#ifndef TARGET_ONLINE_DEBUG_DISABLE
+#ifndef TARGET_ONLINE_DEBUG_DISABLE if(flags & __IEC_DEBUG_FLAG){
/* copy visible variable to buffer */;
/* compute next cursor positon.
No need to check overflow, as BUFFER_SIZE
is computed large enough */
- if((dsc->type == STRING_ENUM) ||
- (dsc->type == STRING_P_ENUM) ||
- (dsc->type == STRING_O_ENUM)){
+ if(__Is_a_string(dsc)){ /* optimization for strings */
size = ((STRING*)visible_value_p)->len + 1;
@@ -174,7 +172,7 @@
memcpy(real_value_p, visible_value_p, size);
if(flags & __IEC_RETAIN_FLAG){
/* compute next cursor positon*/
--- a/targets/var_access.c Fri Dec 25 17:12:02 2020 +0000
+++ b/targets/var_access.c Mon Jan 18 10:59:28 2021 +0100
@@ -14,6 +14,10 @@
forced_value_p = &((__IEC_##TYPENAME##_p *)varp)->fvalue;\
+#define __Is_a_string(dsc) (dsc->type == STRING_ENUM) ||\ + (dsc->type == STRING_P_ENUM) ||\ + (dsc->type == STRING_O_ENUM) static void* UnpackVar(__Unpack_desc_type *dsc, void **real_value_p, char *flags)
--- a/tests/python/plc.xml Fri Dec 25 17:12:02 2020 +0000
+++ b/tests/python/plc.xml Mon Jan 18 10:59:28 2021 +0100
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://www.plcopen.org/xml/tc6_0201" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xsi:schemaLocation="http://www.plcopen.org/xml/tc6_0201">
<fileHeader companyName="" productName="Beremiz" productVersion="0.0" creationDateTime="2008-12-14T16:21:19" contentDescription="This example shows many features in Beremiz: 1. How to implement python extensions. 2. How to implement basic C extension. 3. How to use C code in IEC POUs. 4. How to call C functions from python code. 5. How to avoid race conditions between IEC, C and python code. 6. How to convert betweet different IEC types. "/>
- <contentHeader name="Beremiz Python Support Tests" modificationDateTime="2020-06-17T13:19:14">
+ <contentHeader name="Beremiz Python Support Tests" modificationDateTime="2020-10-19T23:53:08"> <pageSize x="1024" y="1024"/>
--- a/util/ProcessLogger.py Fri Dec 25 17:12:02 2020 +0000
+++ b/util/ProcessLogger.py Mon Jan 18 10:59:28 2021 +0100
@@ -78,7 +78,6 @@
timeout=None, outlimit=None, errlimit=None,
endlog=None, keyword=None, kill_it=False, cwd=None,
encoding=None, output_encoding=None):
if not isinstance(Command, list):
self.Command_str = Command
@@ -160,7 +159,7 @@
v = v.decode(self.output_encoding)
+ if self.logger and not self.no_stdout: if (self.keyword and v.find(self.keyword) != -1) or (self.outlimit and self.outlen > self.outlimit):
@@ -170,7 +169,7 @@
v = v.decode(self.output_encoding)
+ if self.logger and not self.no_stderr: self.logger.write_warning(v)
if self.errlimit and self.errlen > self.errlimit: