--- a/py_ext/PythonFileCTNMixin.py Sat Dec 06 19:31:51 2014 +0000
+++ b/py_ext/PythonFileCTNMixin.py Wed Oct 21 15:00:32 2015 +0100
@@ -7,7 +7,7 @@
from PythonEditor import PythonEditor
class PythonFileCTNMixin(CodeFile):
-
+
CODEFILE_NAME = "PyFile"
SECTIONS_NAMES = [
"globals",
@@ -16,45 +16,45 @@
"start",
"stop"]
EditorType = PythonEditor
-
+
def __init__(self):
CodeFile.__init__(self)
-
+
filepath = self.PythonFileName()
-
+
if os.path.isfile(filepath):
PythonParser = GenerateParserFromXSD(
- os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd"))
-
+ os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd"))
+
xmlfile = open(filepath, 'r')
pythonfile_xml = xmlfile.read()
xmlfile.close()
-
+
pythonfile_xml = pythonfile_xml.replace(
- 'xmlns="http://www.w3.org/2001/XMLSchema"',
+ 'xmlns="http://www.w3.org/2001/XMLSchema"',
'xmlns:xhtml="http://www.w3.org/1999/xhtml"')
for cre, repl in [
(re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
(re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
pythonfile_xml = cre.sub(repl, pythonfile_xml)
-
+
try:
python_code, error = PythonParser.LoadXMLString(pythonfile_xml)
- if error is None:
+ if error is None:
self.CodeFile.globals.setanyText(python_code.getanyText())
os.remove(filepath)
self.CreateCodeFileBuffer(False)
self.OnCTNSave()
except Exception, exc:
error = unicode(exc)
-
+
if error is not None:
self.GetCTRoot().logger.write_error(
- _("Couldn't import old %s file.") % CTNName)
-
+ _("Couldn't import old %s file.") % self.CTNName())
+
def CodeFileName(self):
return os.path.join(self.CTNPath(), "pyfile.xml")
-
+
def PythonFileName(self):
return os.path.join(self.CTNPath(), "py_ext.xml")
@@ -64,30 +64,46 @@
return self.PreSectionsTexts.get(section,"") + "\n" + \
getattr(self.CodeFile, section).getanyText() + "\n" + \
self.PostSectionsTexts.get(section,"")
-
def CTNGenerate_C(self, buildpath, locations):
- # location string for that CTN
- location_str = "_".join(map(lambda x:str(x),
+ # location string for that CTN
+ location_str = "_".join(map(lambda x:str(x),
self.GetCurrentLocation()))
configname = self.GetCTRoot().GetProjectConfigNames()[0]
-
-
+
+ pyextname = self.CTNName()
+ varinfos = map(lambda variable : {
+ "name": variable.getname(),
+ "desc" : repr(variable.getdesc()),
+ "onchangecode" : '"'+variable.getonchange()+\
+ "('"+variable.getname()+"')\"" \
+ if variable.getonchange() else '""',
+ "onchange" : repr(variable.getonchange()) \
+ if variable.getonchange() else None,
+ "opts" : repr(variable.getopts()),
+ "configname" : configname.upper(),
+ "uppername" : variable.getname().upper(),
+ "IECtype" : variable.gettype(),
+ "pyextname" :pyextname},
+ self.CodeFile.variables.variable)
# python side PLC global variables access stub
globalstubs = "\n".join(["""\
_%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
TypeTranslator["%(IECtype)s"]
_PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
_PySafeGetPLCGlob_%(name)s.restype = None
-_PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
+_PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
_PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
_PySafeSetPLCGlob_%(name)s.restype = None
_PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
-""" % { "name": variable.getname(),
- "configname": configname.upper(),
- "uppername": variable.getname().upper(),
- "IECtype": variable.gettype()}
- for variable in self.CodeFile.variables.variable])
+_%(pyextname)sGlobalsDesc.append((
+ "%(name)s",
+ "%(IECtype)s",
+ %(desc)s,
+ %(onchange)s,
+ %(opts)s))
+""" % varinfo
+ for varinfo in varinfos])
# Runtime calls (start, stop, init, and cleanup)
rtcalls = ""
@@ -101,36 +117,41 @@
else:
rtcalls += " pass\n\n"
- globalsection = self.GetSection("globals")
+ globalsection = self.GetSection("globals")
PyFileContent = """\
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## Code generated by Beremiz python mixin confnode
-##
-
+##
+
## Code for PLC global variable access
from targets.typemapping import TypeTranslator
-import ctypes
+import ctypes
+_%(pyextname)sGlobalsDesc = []
+__ext_name__ = "%(pyextname)s"
+PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
%(globalstubs)s
-
+
## User code in "global" scope
%(globalsection)s
## Beremiz python runtime calls
%(rtcalls)s
+del __ext_name__
+
""" % locals()
# write generated content to python file
- runtimefile_path = os.path.join(buildpath,
+ runtimefile_path = os.path.join(buildpath,
"runtime_%s.py"%location_str)
runtimefile = open(runtimefile_path, 'w')
runtimefile.write(PyFileContent.encode('utf-8'))
runtimefile.close()
# C code for safe global variables access
-
+
vardecfmt = """\
extern __IEC_%(IECtype)s_t %(configname)s__%(uppername)s;
IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s;
@@ -151,6 +172,11 @@
}
"""
+
+ vardeconchangefmt = """\
+PYTHON_POLL* __%(name)s_notifier;
+"""
+
varretfmt = """\
if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
if(__%(name)s_wbuffer_written == 1){
@@ -159,34 +185,51 @@
}
AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
}
-"""
+"""
varpubfmt = """\
if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
- __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
+ __%(name)s_rbuffer = __GET_VAR(%(configname)s__%(uppername)s);
AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
}
-"""
-
- var_str = map("\n".join, zip(*[
- map(lambda f : f % varinfo,
- (vardecfmt, varretfmt, varpubfmt))
- for varinfo in map(lambda variable : {
- "name": variable.getname(),
- "configname": configname.upper(),
- "uppername": variable.getname().upper(),
- "IECtype": variable.gettype()},
- self.CodeFile.variables.variable)]))
- if len(var_str) > 0:
- vardec, varret, varpub = var_str
- else:
- vardec = varret = varpub = ""
-
+"""
+
+ varpubonchangefmt = """\
+ if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
+ IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
+ if(__%(name)s_rbuffer != tmp){
+ __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
+ PYTHON_POLL_body__(__%(name)s_notifier);
+ }
+ 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));
+"""
+ vardec = "\n".join([(vardecfmt + vardeconchangefmt
+ if varinfo["onchange"] else vardecfmt)% varinfo
+ for varinfo in varinfos])
+ varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
+ varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
+ varpubfmt) % varinfo
+ for varinfo in varinfos])
+ varinit = "\n".join([varinitonchangefmt % dict(
+ onchangelen = len(varinfo["onchangecode"]),**varinfo)
+ for varinfo in varinfos if varinfo["onchange"]])
+
+ # TODO : use config name obtained from model instead of default
+ # "config.h". User cannot change config name, but project imported
+ # or created in older beremiz vesion could use different name.
PyCFileContent = """\
-/*
- * Code generated by Beremiz py_ext confnode
+/*
+ * Code generated by Beremiz py_ext confnode
* for safe global variables access
*/
#include "iec_types_all.h"
+#include "POUS.h"
+#include "config.h"
#include "beremiz.h"
/* User variables reference */
@@ -194,6 +237,7 @@
/* Beremiz confnode functions */
int __init_%(location_str)s(int argc,char **argv){
+%(varinit)s
return 0;
}
@@ -208,15 +252,15 @@
%(varpub)s
}
""" % locals()
-
+
Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str)
pycfile = open(Gen_PyCfile_path,'w')
pycfile.write(PyCFileContent)
pycfile.close()
-
+
matiec_flags = '"-l -p -I%s"'%os.path.abspath(
self.GetCTRoot().GetIECLibPath())
-
+
return ([(Gen_PyCfile_path, matiec_flags)],
"",
True,