py_ext/PythonFileCTNMixin.py
author Laurent Bessard
Wed, 15 May 2013 23:11:49 +0200
changeset 1148 d0e40e99f422
parent 1145 203f4eff3313
child 1154 da9ccfceff31
permissions -rw-r--r--
Fixed bug when generating py_ext code when no variable defined
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 = None
_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(),
        "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;
void __SafeGetPLCGlob_%(name)s(%(IECtype)s *pvalue){
    %(IECtype)s res;
    while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
    *pvalue = __%(name)s_rbuffer;
    AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
}
__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);
    }
""" 

        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": "IEC_%s"%variable.gettype(),
                    "initial" : str(variable.getinitial())},
                    self.CodeFile.variables.variable)]))
        if len(var_str) > 0:
            vardec, varret, varpub = var_str
        else:
            vardec = varret = varpub = ""
        
        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)],
                "",
                True,
                ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")))