PLCGenerator.py
changeset 125 394d9f168258
parent 108 9aa1fdfb7cb2
child 128 d16a8df4d322
--- a/PLCGenerator.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/PLCGenerator.py	Thu Dec 06 18:05:29 2007 +0100
@@ -34,6 +34,7 @@
 
 currentProject = None
 currentProgram = ""
+datatypeComputed = {}
 pouComputed = {}
 
 def ReIndentText(text, nb_spaces):
@@ -53,6 +54,49 @@
                 compute += "\n"
     return compute
 
+def GenerateDataType(datatype_name):
+    if not datatypeComputed.get(datatype_name, True):
+        datatypeComputed[datatype_name] = True
+        global currentProject, currentProgram
+        datatype = currentProject.getDataType(datatype_name)
+        datatype_def = "  %s :"%datatype.getName()
+        basetype_content = datatype.baseType.getContent()
+        if basetype_content["value"] is None:
+            datatype_def += " %s"%basetype_content["name"]
+        elif basetype_content["name"] == "derived":
+            basetype_name = basetype_content["value"].getName()
+            GenerateDataType(basetype_name)
+            datatype_def += " %s"%basetype_name
+        elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
+            base_type = basetype_content["value"].baseType.getContent()
+            if base_type["value"] is None:
+                basetype_name = base_type["name"]
+            else:
+                basetype_name = base_type["value"].getName()
+                GenerateDataType(basetype_name)
+            min_value = basetype_content["value"].range.getLower()
+            max_value = basetype_content["value"].range.getUpper()
+            datatype_def += " %s (%d..%d)"%(basetype_name, min_value, max_value)
+        elif basetype_content["name"] == "enum":
+            values = []
+            for value in basetype_content["value"].values.getValue():
+                values.append(value.getName())
+            datatype_def += " (%s)"%", ".join(values)
+        elif basetype_content["name"] == "array":
+            base_type = basetype_content["value"].baseType.getContent()
+            if base_type["value"] is None:
+                basetype_name = base_type["name"]
+            else:
+                basetype_name = base_type["value"].getName()
+                GenerateDataType(basetype_name)
+            dimensions = []
+            for dimension in basetype_content["value"].getDimension():
+                dimensions.append("0..%d"%(dimension.getUpper() - 1))
+            datatype_def += " ARRAY [%s] OF %s"%(",".join(dimensions), basetype_name)
+        if datatype.initialValue is not None:
+            datatype_def += " := %s"%str(datatype.initialValue.getValue())
+        currentProgram += "%s;\n"%datatype_def
+
 def GeneratePouProgram(pou_name):
     if not pouComputed.get(pou_name, True):
         pouComputed[pou_name] = True
@@ -64,6 +108,8 @@
         else:
             raise ValueError, "Undefined pou type"
         pou_program.GenerateInterface(pou.getInterface())
+        pou_program.GenerateConnectionTypes(pou)
+        #print pou.getName(), pou_program.ConnectionTypes, pou_program.RelatedConnections
         pou_program.GenerateProgram(pou)
         currentProgram += pou_program.GenerateSTProgram()
 
@@ -77,7 +123,11 @@
             config += " CONSTANT"
         config += "\n"
         for var in varlist.getVariable():
-            var_type = var.getType().getValue()
+            vartype_content = var.getType().getContent()
+            if vartype_content["value"] is None:
+                var_type = vartype_content["name"]
+            else:
+                var_type = vartype_content["value"].getName()
             config += "    %s "%var.getName()
             address = var.getAddress()
             if address:
@@ -110,12 +160,16 @@
             resrce += " CONSTANT"
         resrce += "\n"
         for var in varlist.getVariable():
-            var_type = var.getType().getValue()
+            vartype_content = var.getType().getContent()
+            if vartype_content["value"] is None:
+                var_type = vartype_content["name"]
+            else:
+                var_type = vartype_content["value"].getName()
             resrce += "      %s "%var.getName()
             address = var.getAddress()
             if address:
                 resrce += "AT %s "%address
-            resrce += ": %s"%var.getType().getValue()
+            resrce += ": %s"%var_type
             initial = var.getInitialValue()
             if initial:
                 value = str(initial.getValue())
@@ -171,6 +225,8 @@
         self.InitialSteps = []
         self.ComputedBlocks = {}
         self.ComputedConnectors = {}
+        self.ConnectionTypes = {}
+        self.RelatedConnections = []
         self.SFCNetworks = {"Steps":{}, "Transitions":{}, "Actions":{}}
         self.SFCComputedBlocks = ""
         self.ActionNumber = 0
@@ -187,22 +243,71 @@
                     return True
         return False
     
+    def GetVariableType(self, name):
+        for list_type, retain, constant, located, vars in self.Interface:
+            for var_type, var_name, var_address, var_initial in vars:
+                if name == var_name:
+                    return var_type
+        return None
+    
+    def GetConnectedConnection(self, connection, body):
+        links = connection.getConnections()
+        if links and len(links) == 1:
+            return self.GetLinkedConnection(links[0], body)
+        return None
+        
+    def GetLinkedConnection(self, link, body):
+        parameter = link.getFormalParameter()
+        instance = body.getContentInstance(link.getRefLocalId())
+        if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable, plcopen.continuation, plcopen.contact, plcopen.coil)):
+            return instance.connectionPointOut
+        elif isinstance(instance, plcopen.block):
+            outputvariables = instance.outputVariables.getVariable()
+            if len(outputvariables) == 1:
+                return outputvariables[0].connectionPointOut
+            elif parameter:
+                for variable in outputvariables:
+                    if variable.getFormalParameter() == parameter:
+                        return variable.connectionPointOut
+            else:
+                point = link.getPosition()[-1]
+                for variable in outputvariables:
+                    relposition = variable.connectionPointOut.getRelPosition()
+                    blockposition = instance.getPosition()
+                    if point.x == blockposition.x + relposition[0] and point.y == blockposition.y + relposition[1]:
+                        return variable.connectionPointOut
+        elif isinstance(instance, plcopen.leftPowerRail):
+            outputconnections = instance.getConnectionPointOut()
+            if len(outputconnections) == 1:
+                return outputconnections[0]
+            else:
+                point = link.getPosition()[-1]
+                for outputconnection in outputconnections:
+                    relposition = outputconnection.getRelPosition()
+                    powerrailposition = instance.getPosition()
+                    if point.x == powerrailposition.x + relposition[0] and point.y == powerrailposition.y + relposition[1]:
+                        return outputconnection
+        return None
+        
+    def ExtractRelatedConnections(self, connection):
+        for i, related in enumerate(self.RelatedConnections):
+            if connection in related:
+                return self.RelatedConnections.pop(i)
+        return [connection]
+    
     def GenerateInterface(self, interface):
         if self.Type == "FUNCTION":
-            self.ReturnType = interface.getReturnType().getValue()
+            returntype_content = interface.getReturnType().getContent()
+            if returntype_content["value"] is None:
+                self.ReturnType = returntype_content["name"]
+            else:
+                self.ReturnType = returntype_content["value"].getName()
         for varlist in interface.getContent():
             variables = []
             located = False
             for var in varlist["value"].getVariable():
-                type = var.getType().getValue()
-                if not isinstance(type, (StringType, UnicodeType)):
-                    type = type.getName()
-                    GeneratePouProgram(type)
-                    blocktype = GetBlockType(type)
-                    if blocktype:
-                        variables.extend(blocktype["initialise"](type, var.getName()))
-                        located = False
-                else:
+                vartype_content = var.getType().getContent()
+                if vartype_content["value"] is None:
                     initial = var.getInitialValue()
                     if initial:
                         initial_value = initial.getValue()
@@ -211,11 +316,120 @@
                     address = var.getAddress()
                     if address:
                         located = True
-                    variables.append((type, var.getName(), address, initial_value))
+                    variables.append((vartype_content["name"], var.getName(), address, initial_value))
+                else:
+                    var_type = vartype_content["value"].getName()
+                    GeneratePouProgram(var_type)
+                    blocktype = GetBlockType(var_type)
+                    if blocktype is not None:
+                        variables.extend(blocktype["initialise"](var_type, var.getName()))
+                        located = False
+                    else:
+                        initial = var.getInitialValue()
+                        if initial:
+                            initial_value = initial.getValue()
+                        else:
+                            initial_value = None
+                        address = var.getAddress()
+                        if address:
+                            located = True
+                        variables.append((vartype_content["value"].getName(), var.getName(), address, initial_value))
             if len(variables) > 0:
                 self.Interface.append((varTypeNames[varlist["name"]], varlist["value"].getRetain(), 
                             varlist["value"].getConstant(), located, variables))
     
+    def GenerateConnectionTypes(self, pou):
+        body = pou.getBody()
+        body_content = body.getContent()
+        body_type = body_content["name"]
+        if body_type in ["FBD", "LD", "SFC"]:
+            for instance in body.getContentInstances():
+                if isinstance(instance, (plcopen.inVariable, plcopen.outVariable, plcopen.inOutVariable)):
+                    expression = instance.getExpression()
+                    var_type = self.GetVariableType(expression)
+                    if expression == pou.getName():
+                        returntype_content = pou.interface.getReturnType().getContent()
+                        if returntype_content["value"] is None:
+                            var_type = returntype_content["name"]
+                        else:
+                            var_type = returntype_content["value"].getName()
+                    elif var_type is None:
+                        var_type = expression.split("#")[0]
+                    if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable)):
+                        self.ConnectionTypes[instance.connectionPointOut] = var_type
+                    if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable)):
+                        self.ConnectionTypes[instance.connectionPointIn] = var_type
+                        connected = self.GetConnectedConnection(instance.connectionPointIn, body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = var_type
+                elif isinstance(instance, (plcopen.contact, plcopen.coil)):
+                    self.ConnectionTypes[instance.connectionPointOut] = "BOOL"
+                    self.ConnectionTypes[instance.connectionPointIn] = "BOOL"
+                    connected = self.GetConnectedConnection(instance.connectionPointIn, body)
+                    if connected and connected not in self.ConnectionTypes:
+                        for connection in self.ExtractRelatedConnections(connected):
+                            self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.leftPowerRail):
+                    for connection in instance.getConnectionPointOut():
+                        self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.rightPowerRail):
+                    for connection in instance.getConnectionPointIn():
+                        self.ConnectionTypes[connection] = "BOOL"
+                        connected = self.GetConnectedConnection(connection, body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.transition):
+                    content = instance.condition.getContent()
+                    if content["name"] == "connection" and len(content["value"]) == 1:
+                        connected = self.GetLinkedConnection(content["value"][0], body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.block):
+                    block_infos = GetBlockType(instance.getTypeName())
+                    undefined = {}
+                    for variable in instance.outputVariables.getVariable():
+                        output_name = variable.getFormalParameter()
+                        for oname, otype, oqualifier in block_infos["outputs"]:
+                            if output_name == oname and variable.connectionPointOut not in self.ConnectionTypes:
+                                if otype.startswith("ANY"):
+                                    if otype not in undefined:
+                                        undefined[otype] = []
+                                    undefined[otype].append(variable.connectionPointOut)
+                                else:
+                                    for connection in self.ExtractRelatedConnections(variable.connectionPointOut):
+                                        self.ConnectionTypes[connection] = otype
+                    for variable in instance.inputVariables.getVariable():
+                        input_name = variable.getFormalParameter()
+                        for iname, itype, iqualifier in block_infos["inputs"]:
+                            if input_name == iname:
+                                connected = self.GetConnectedConnection(variable.connectionPointIn, body)
+                                if itype.startswith("ANY"):
+                                    if itype not in undefined:
+                                        undefined[itype] = []
+                                    undefined[itype].append(variable.connectionPointIn)
+                                    if connected:
+                                        undefined[itype].append(connected)
+                                else:
+                                    self.ConnectionTypes[variable.connectionPointIn] = itype
+                                    if connected and connected not in self.ConnectionTypes:
+                                        for connection in self.ExtractRelatedConnections(connected):
+                                            self.ConnectionTypes[connection] = itype
+                    for var_type, connections in undefined.items():
+                        related = []
+                        for connection in connections:
+                            if connection in self.ConnectionTypes:
+                                var_type = self.ConnectionTypes[connection]
+                            else:
+                                related.extend(self.ExtractRelatedConnections(connection))
+                        if var_type.startswith("ANY") and len(related) > 0:
+                            self.RelatedConnections.append(related)
+                        else:
+                            for connection in related:
+                                self.ConnectionTypes[connection] = var_type
+                                    
     def GenerateProgram(self, pou):
         body = pou.getBody()
         body_content = body.getContent()
@@ -223,7 +437,20 @@
         if body_type in ["IL","ST"]:
             self.Program = ReIndentText(body_content["value"].getText(), 2)
         elif body_type == "FBD":
+            orderedInstances = []
+            otherInstances = []
             for instance in body.getContentInstances():
+                if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable, plcopen.block)):
+                    executionOrderId = instance.getExecutionOrderId()
+                    if executionOrderId > 0:
+                        orderedInstances.append((executionOrderId, instance))
+                    else:
+                        otherInstances.append(instance)
+                elif isinstance(instance, plcopen.connector):
+                    otherInstances.append(instance)
+            orderedInstances.sort()
+            instances = [instance for (executionOrderId, instance) in orderedInstances] + otherInstances
+            for instance in instances:
                 if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable)):
                     var = instance.getExpression()
                     connections = instance.connectionPointIn.getConnections()
@@ -231,8 +458,9 @@
                         expression = self.ComputeFBDExpression(body, connections[0])
                         self.Program += "  %s := %s;\n"%(var, expression)
                 elif isinstance(instance, plcopen.block):
-                    type = instance.getTypeName()
-                    block_infos = GetBlockType(type)
+                    block_type = instance.getTypeName()
+                    self.GeneratePouProgram(block_type)
+                    block_infos = GetBlockType(block_type)
                     block_infos["generate"](self, instance, body, None)
                 elif isinstance(instance, plcopen.connector):
                     connector = instance.getName()
@@ -271,15 +499,16 @@
             for initialstep in self.InitialSteps:
                 self.ComputeSFCStep(initialstep)
     
-    def ComputeFBDExpression(self, body, link):
+    def ComputeFBDExpression(self, body, link, order = False):
         localid = link.getRefLocalId()
         instance = body.getContentInstance(localid)
         if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable)):
             return instance.getExpression()
         elif isinstance(instance, plcopen.block):
             block_type = instance.getTypeName()
+            self.GeneratePouProgram(block_type)
             block_infos = GetBlockType(block_type)
-            return block_infos["generate"](self, instance, body, link)
+            return block_infos["generate"](self, instance, body, link, order)
         elif isinstance(instance, plcopen.continuation):
             name = instance.getName()
             computed_value = self.ComputedConnectors.get(name, None)
@@ -290,7 +519,7 @@
                     if tmp_instance.getName() == name:
                         connections = tmp_instance.connectionPointIn.getConnections()
                         if connections and len(connections) == 1:
-                            expression = self.ComputeFBDExpression(body, connections[0])
+                            expression = self.ComputeFBDExpression(body, connections[0], order)
                             self.ComputedConnectors[name] = expression
                             return expression
             raise ValueError, "No connector found"
@@ -304,6 +533,7 @@
                 paths.append(None)
             elif isinstance(next, plcopen.block):
                 block_type = next.getTypeName()
+                self.GeneratePouProgram(block_type)
                 block_infos = GetBlockType(block_type)
                 paths.append(block_infos["generate"](self, next, body, connection))
             else:
@@ -662,8 +892,15 @@
     global currentProject, currentProgram
     currentProject = project
     currentProgram = ""
+    for datatype in project.getDataTypes():
+        datatypeComputed[datatype.getName()] = False
     for pou in project.getPous():
         pouComputed[pou.getName()] = False
+    if len(datatypeComputed) > 0:
+        currentProgram += "TYPE\n"
+        for datatype_name in datatypeComputed.keys():
+            GenerateDataType(datatype_name)
+        currentProgram += "END_TYPE\n\n"
     for pou_name in pouComputed.keys():
         GeneratePouProgram(pou_name)
     for config in project.getConfigurations():