Added more columns in variable list of extensions' code editors - updating python's PLCGlobals from PLC logic can trigger asynchronous 'OnChange' python call
authorEdouard Tisserant
Thu, 19 Feb 2015 00:40:53 +0100
changeset 1448 20ff66dcc31d
parent 1447 d6b878525ceb
child 1449 5f09fa31d7b0
Added more columns in variable list of extensions' code editors - updating python's PLCGlobals from PLC logic can trigger asynchronous 'OnChange' python call
CodeFileTreeNode.py
ProjectController.py
editors/CodeFileEditor.py
py_ext/PythonFileCTNMixin.py
tests/wamp/beremiz.xml
tests/wamp/plc.xml
--- a/CodeFileTreeNode.py	Tue Feb 17 10:23:57 2015 +0100
+++ b/CodeFileTreeNode.py	Thu Feb 19 00:40:53 2015 +0100
@@ -31,6 +31,9 @@
                     </xsd:simpleType>
                   </xsd:attribute>
                   <xsd:attribute name="initial" type="xsd:string" use="optional" default=""/>
+                  <xsd:attribute name="desc" type="xsd:string" use="optional" default=""/>
+                  <xsd:attribute name="onchange" type="xsd:string" use="optional" default=""/>
+                  <xsd:attribute name="opts" type="xsd:string" use="optional" default=""/>
                 </xsd:complexType>
               </xsd:element>
             </xsd:sequence>
@@ -119,6 +122,9 @@
             variable.setname(var["Name"])
             variable.settype(var["Type"])
             variable.setinitial(var["Initial"])
+            variable.setdesc(var["Description"])
+            variable.setonchange(var["OnChange"])
+            variable.setopts(var["Options"])
             self.CodeFile.variables.appendvariable(variable)
     
     def GetVariables(self):
@@ -126,7 +132,11 @@
         for var in self.CodeFileVariables(self.CodeFile):
             datas.append({"Name" : var.getname(), 
                           "Type" : var.gettype(), 
-                          "Initial" : var.getinitial()})
+                          "Initial" : var.getinitial(),
+                          "Description" : var.getdesc(),
+                          "OnChange"    : var.getonchange(),
+                          "Options"     : var.getopts(),
+                         })
         return datas
 
     def SetTextParts(self, parts):
@@ -157,11 +167,15 @@
         return True
 
     def CTNGlobalInstances(self):
-        current_location = self.GetCurrentLocation()
-        return [(variable.getname(),
+        variables = self.CodeFileVariables(self.CodeFile)
+        ret =  [(variable.getname(),
                  variable.gettype(),
-                 variable.getinitial())
-                for variable in self.CodeFileVariables(self.CodeFile)]
+                 variable.getinitial()) 
+                for variable in variables]
+        ret.extend([("On"+variable.getname()+"Change", "python_poll", "")
+                for variable in variables
+                if variable.getonchange()])
+        return ret
 
 #-------------------------------------------------------------------------------
 #                      Current Buffering Management Functions
--- a/ProjectController.py	Tue Feb 17 10:23:57 2015 +0100
+++ b/ProjectController.py	Thu Feb 19 00:40:53 2015 +0100
@@ -982,7 +982,7 @@
             self.ResetBuildMD5()
             return False
 
-        self.LocationCFilesAndCFLAGS =  CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
+        self.LocationCFilesAndCFLAGS =  LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS
         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
         ExtraFiles = CTNExtraFiles + LibExtraFiles
 
--- a/editors/CodeFileEditor.py	Tue Feb 17 10:23:57 2015 +0100
+++ b/editors/CodeFileEditor.py	Thu Feb 19 00:40:53 2015 +0100
@@ -607,7 +607,7 @@
                 renderer = None
                 colname = self.GetColLabelValue(col, False)
 
-                if colname in ["Name", "Initial"]:
+                if colname in ["Name", "Initial", "Description", "OnChange", "Options"]:
                     editor = wx.grid.GridCellTextEditor()
                 elif colname == "Class":
                     editor = wx.grid.GridCellChoiceEditor()
@@ -658,10 +658,13 @@
         self.ParentWindow = window
         self.Controler = controler
 
-        self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": ""}
-        self.Table = VariablesTable(self, [], ["#", "Name", "Type", "Initial"])
-        self.ColAlignements = [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
-        self.ColSizes = [40, 200, 150, 150]
+        self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": "",
+                                      "Description":"", "OnChange":"", "Options":""}
+        self.Table = VariablesTable(self, [], ["#", "Name", "Type", "Initial",
+                                    "Description", "OnChange", "Options"])
+        self.ColAlignements = [wx.ALIGN_RIGHT] +  \
+                              [wx.ALIGN_LEFT]*(len(self.VariablesDefaultValue))
+        self.ColSizes = [20, 150] + [100]*(len(self.VariablesDefaultValue)-1)
         self.VariablesGrid.SetTable(self.Table)
         self.VariablesGrid.SetButtons({"Add": self.AddVariableButton,
                                        "Delete": self.DeleteVariableButton,
--- a/py_ext/PythonFileCTNMixin.py	Tue Feb 17 10:23:57 2015 +0100
+++ b/py_ext/PythonFileCTNMixin.py	Thu Feb 19 00:40:53 2015 +0100
@@ -65,7 +65,6 @@
                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),
@@ -73,7 +72,17 @@
         configname = self.GetCTRoot().GetProjectConfigNames()[0]
 
         pyextname = self.CTNName()
-
+        varinfos = map(lambda variable : {
+                    "name": variable.getname(),
+                    "desc" : repr(variable.getdesc()),   
+                    "onchange" : '"'+variable.getonchange()+"('"+variable.getname()+"')\"" \
+                                 if variable.getonchange() else "",
+                    "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 = \\
@@ -84,13 +93,12 @@
 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
 _PySafeSetPLCGlob_%(name)s.restype = None
 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
-_%(pyextname)sGlobalsDesc.append(("%(name)s","%(IECtype)s"))
-""" % { "name": variable.getname(),
-        "configname": configname.upper(),
-        "uppername": variable.getname().upper(),
-        "IECtype": variable.gettype(),
-        "pyextname":pyextname}
-            for variable in self.CodeFile.variables.variable])
+_%(pyextname)sGlobalsDesc.append((
+    "%(name)s",
+    "%(IECtype)s",
+    %(desc)s,
+    %(opts)s))
+""" % varinfo for varinfo in varinfos])
 
         # Runtime calls (start, stop, init, and cleanup)
         rtcalls = ""
@@ -156,6 +164,11 @@
 }
 
 """
+
+        vardeconchangefmt = """\
+PYTHON_POLL* __%(name)s_notifier;
+"""
+
         varretfmt = """\
     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
         if(__%(name)s_wbuffer_written == 1){
@@ -167,24 +180,36 @@
 """
         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,%(onchange)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["onchange"]),**varinfo)
+                            for varinfo in varinfos if varinfo["onchange"]])
 
         PyCFileContent = """\
 /*
@@ -192,6 +217,8 @@
  * for safe global variables access
  */
 #include "iec_types_all.h"
+#include "POUS.h"
+#include "config.h"
 #include "beremiz.h"
 
 /* User variables reference */
@@ -199,6 +226,7 @@
 
 /* Beremiz confnode functions */
 int __init_%(location_str)s(int argc,char **argv){
+%(varinit)s
     return 0;
 }
 
--- a/tests/wamp/beremiz.xml	Tue Feb 17 10:23:57 2015 +0100
+++ b/tests/wamp/beremiz.xml	Thu Feb 19 00:40:53 2015 +0100
@@ -1,4 +1,4 @@
 <?xml version='1.0' encoding='utf-8'?>
-<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="WAMP://127.0.0.1:8888#Automation#wamptest">
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="WAMP://127.0.0.1:8888#Automation#2534667845">
   <TargetType/>
 </BeremizRoot>
--- a/tests/wamp/plc.xml	Tue Feb 17 10:23:57 2015 +0100
+++ b/tests/wamp/plc.xml	Thu Feb 19 00:40:53 2015 +0100
@@ -1,7 +1,7 @@
 <?xml version='1.0' encoding='utf-8'?>
 <project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
   <fileHeader companyName="Beremiz" productName="Beremiz" productVersion="1" creationDateTime="2015-02-05T11:44:55" contentDescription=" &#10;&#10;"/>
-  <contentHeader name="WAMPTest" modificationDateTime="2015-02-07T22:25:01">
+  <contentHeader name="WAMPTest" modificationDateTime="2015-02-18T23:59:50">
     <coordinateInfo>
       <fbd>
         <scaling x="0" y="0"/>