py_ext/PythonFileCTNMixin.py
author Edouard Tisserant
Wed, 15 May 2013 17:13:49 +0900
changeset 1144 21475ee0e688
parent 1132 28f96aa9c070
child 1145 203f4eff3313
permissions -rw-r--r--
Added stub code and declarations for bidirectional access to PLC globals from python code (untested)
import os
from PLCControler import UndoBuffer
from PythonEditor import PythonEditor

from xml.dom import minidom
from xmlclass import GenerateClassesFromXSD
import cPickle

from CodeFileTreeNode import CodeFile

PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) 

class PythonFileCTNMixin(CodeFile):
    
    CODEFILE_NAME = "PyFile"
    SECTIONS_NAMES = [
        "globals",
        "init",
        "cleanup",
        "start",
        "stop"]
    EditorType = PythonEditor
    
    def __init__(self):
        CodeFile.__init__(self)
        
        filepath = self.PythonFileName()
        
        python_code = PythonClasses["Python"]()
        if os.path.isfile(filepath):
            xmlfile = open(filepath, 'r')
            tree = minidom.parse(xmlfile)
            xmlfile.close()
            
            for child in tree.childNodes:
                if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python":
                    python_code.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
                    self.CodeFile.globals.settext(python_code.gettext())
                    os.remove(filepath)
                    self.CreateCodeFileBuffer(False)
                    self.OnCTNSave()
    
    def CodeFileName(self):
        return os.path.join(self.CTNPath(), "pyfile.xml")
    
    def PythonFileName(self):
        return os.path.join(self.CTNPath(), "py_ext.xml")

    PreSectionsTexts = {}
    PostSectionsTexts = {}
    def GetSection(self,section):
        return self.PreSectionsTexts.get(section,"") + "\n" + \
               getattr(self.CodeFile, section).gettext() + "\n" + \
               self.PostSectionsTexts.get(section,"")
        

    def CTNGenerate_C(self, buildpath, locations):
        # location string for that CTN 
        location_str = "_".join(map(lambda x:str(x), 
                                self.GetCurrentLocation()))
        configname = self.GetCTRoot().GetProjectConfigNames()[0]
        
        
        # 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 = _%(name)s_ctype 
_PySafeGetPLCGlob_%(name)s.argtypes = []
_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(),
        "initial" : str(variable.getinitial())}
            for variable in self.CodeFile.variables.variable])

        # Runtime calls (start, stop, init, and cleanup)
        rtcalls = ""
        for section in self.SECTIONS_NAMES:
            if section != "globals":
                rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
                sectiontext = self.GetSection(section).strip()
                if sectiontext:
                    rtcalls += '    ' + \
                        sectiontext.strip().replace('\n', '\n    ')+"\n"
                else:
                    rtcalls += "    pass\n\n"

        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 
%(globalstubs)s
        
## User code in "global" scope
%(globalsection)s

## Beremiz python runtime calls
%(rtcalls)s

""" % locals()

        # write generated content to python file
        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  __%(IECtype)s_t %(configname)s__%(uppername)s;
%(IECtype)s __%(name)s_rbuffer = %(initial)s;
%(IECtype)s __%(name)s_wbuffer;
long __%(name)s_rlock = 0;
long __%(name)s_wlock = 0;
int __%(name)s_wbuffer_written = 0;
%(IECtype)s __SafeGetPLCGlob_%(name)s(){
    %(IECtype)s res;
    while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
    res = __%(name)s_rbuffer;
    AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
    return res;
}
__SafeSetPLCGlob_%(name)s(%(IECtype)s *value){
    while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1));
    __%(name)s_wbuffer = *value;
    __%(name)s_wbuffer_written = 1;
    AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
}

"""
        varretfmt = """\
    if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
        if(__%(name)s_wbuffer_written == 1){
            %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
            __%(name)s_wbuffer_written = 0;
        }
        AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
    }
""" 
        varpubfmt = """\
    if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
        __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
        AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
    }
""" 

        vardec, varret, varpub = 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": "IEC_%s"%variable.gettype(),
                    "initial" : str(variable.getinitial())},
                    self.CodeFile.variables.variable)]))
        
        PyCFileContent = """\
/* 
 * Code generated by Beremiz py_ext confnode 
 * for safe global variables access
 */
#include "iec_types_all.h"
#include "beremiz.h"

/* User variables reference */
%(vardec)s

/* Beremiz confnode functions */
int __init_%(location_str)s(int argc,char **argv){
    return 0;
}

void __cleanup_%(location_str)s(void){
}

void __retrieve_%(location_str)s(void){
%(varret)s
}

void __publish_%(location_str)s(void){
%(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 = '"-I%s"'%os.path.abspath(
            self.GetCTRoot().GetIECLibPath())
        
        return ([(Gen_PyCfile_path, matiec_flags)],
                "",
                False,
                ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")))