plcopen/structures.py
changeset 814 5743cbdff669
child 949 aa24cf3b7009
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 import string, os, sys
       
     5 
       
     6 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     7 #based on the plcopen standard. 
       
     8 #
       
     9 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
    10 #
       
    11 #See COPYING file for copyrights details.
       
    12 #
       
    13 #This library is free software; you can redistribute it and/or
       
    14 #modify it under the terms of the GNU General Public
       
    15 #License as published by the Free Software Foundation; either
       
    16 #version 2.1 of the License, or (at your option) any later version.
       
    17 #
       
    18 #This library is distributed in the hope that it will be useful,
       
    19 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    20 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    21 #General Public License for more details.
       
    22 #
       
    23 #You should have received a copy of the GNU General Public
       
    24 #License along with this library; if not, write to the Free Software
       
    25 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    26 
       
    27 
       
    28 LANGUAGES = ["IL","ST","FBD","LD","SFC"]
       
    29 
       
    30 LOCATIONDATATYPES = {"X" : ["BOOL"],
       
    31                      "B" : ["SINT", "USINT", "BYTE", "STRING"],
       
    32                      "W" : ["INT", "UINT", "WORD", "WSTRING"],
       
    33                      "D" : ["DINT", "UDINT", "REAL", "DWORD"],
       
    34                      "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} 
       
    35 
       
    36 _ = lambda x:x
       
    37 
       
    38 # Helper for emulate join on element list
       
    39 def JoinList(separator, mylist):
       
    40     if len(mylist) > 0 :
       
    41         return reduce(lambda x, y: x + separator + y, mylist)
       
    42     else :
       
    43         return mylist
       
    44 
       
    45 def generate_block(generator, block, block_infos, body, link, order=False, to_inout=False):
       
    46     body_type = body.getcontent()["name"]
       
    47     name = block.getinstanceName()
       
    48     type = block.gettypeName()
       
    49     executionOrderId = block.getexecutionOrderId()
       
    50     inout_variables = {}
       
    51     for input_variable in block.inputVariables.getvariable():
       
    52         for output_variable in block.outputVariables.getvariable():
       
    53             if input_variable.getformalParameter() == output_variable.getformalParameter():
       
    54                 inout_variables[input_variable.getformalParameter()] = ""
       
    55     if block_infos["type"] == "function":
       
    56         output_variables = block.outputVariables.getvariable()
       
    57         if not generator.ComputedBlocks.get(block, False) and not order:
       
    58             generator.ComputedBlocks[block] = True
       
    59             vars = []
       
    60             one_input_connected = False
       
    61             for i, variable in enumerate(block.inputVariables.getvariable()):
       
    62                 input_info = (generator.TagName, "block", block.getlocalId(), "input", i)
       
    63                 connections = variable.connectionPointIn.getconnections()
       
    64                 if connections is not None:
       
    65                     parameter = variable.getformalParameter()
       
    66                     if parameter != "EN":
       
    67                         one_input_connected = True
       
    68                     if inout_variables.has_key(parameter):
       
    69                         value = generator.ComputeExpression(body, connections, executionOrderId > 0, True)
       
    70                         inout_variables[parameter] = value
       
    71                     else:
       
    72                         value = generator.ComputeExpression(body, connections, executionOrderId > 0)
       
    73                     if len(output_variables) > 1:
       
    74                         vars.append([(parameter, input_info),
       
    75                                      (" := ", ())] + generator.ExtractModifier(variable, value, input_info))
       
    76                     else:
       
    77                         vars.append(generator.ExtractModifier(variable, value, input_info))
       
    78             if one_input_connected:
       
    79                 for i, variable in enumerate(output_variables):
       
    80                     parameter = variable.getformalParameter()
       
    81                     if not inout_variables.has_key(parameter):
       
    82                         if variable.getformalParameter() == "":
       
    83                             variable_name = "%s%d"%(type, block.getlocalId())
       
    84                         else:
       
    85                             variable_name = "%s%d_%s"%(type, block.getlocalId(), parameter)
       
    86                         if generator.Interface[-1][0] != "VAR" or generator.Interface[-1][1] is not None or generator.Interface[-1][2]:
       
    87                             generator.Interface.append(("VAR", None, False, []))
       
    88                         if variable.connectionPointOut in generator.ConnectionTypes:
       
    89                             generator.Interface[-1][3].append((generator.ConnectionTypes[variable.connectionPointOut], variable_name, None, None))
       
    90                         else:
       
    91                             generator.Interface[-1][3].append(("ANY", variable_name, None, None))
       
    92                         if len(output_variables) > 1 and parameter not in ["", "OUT"]:
       
    93                             vars.append([(parameter, (generator.TagName, "block", block.getlocalId(), "output", i)), 
       
    94                                          (" => %s"%variable_name, ())])
       
    95                         else:
       
    96                             output_info = (generator.TagName, "block", block.getlocalId(), "output", i)
       
    97                             output_name = variable_name
       
    98                 generator.Program += [(generator.CurrentIndent, ()),
       
    99                                       (output_name, output_info),
       
   100                                       (" := ", ()),
       
   101                                       (type, (generator.TagName, "block", block.getlocalId(), "type")),
       
   102                                       ("(", ())]
       
   103                 generator.Program += JoinList([(", ", ())], vars)
       
   104                 generator.Program += [(");\n", ())]
       
   105             else:
       
   106                 generator.Warnings.append(_("\"%s\" function cancelled in \"%s\" POU: No input connected")%(type, generator.TagName.split("::")[-1]))
       
   107         if link:
       
   108             connectionPoint = link.getposition()[-1]
       
   109         else:
       
   110             connectionPoint = None
       
   111         for i, variable in enumerate(output_variables):
       
   112             blockPointx, blockPointy = variable.connectionPointOut.getrelPositionXY()
       
   113             if not connectionPoint or block.getx() + blockPointx == connectionPoint.getx() and block.gety() + blockPointy == connectionPoint.gety():
       
   114                 output_info = (generator.TagName, "block", block.getlocalId(), "output", i)
       
   115                 parameter = variable.getformalParameter()
       
   116                 if inout_variables.has_key(parameter):
       
   117                     output_value = inout_variables[parameter]
       
   118                 else:
       
   119                     if parameter == "":
       
   120                         output_name = "%s%d"%(type, block.getlocalId())
       
   121                     else:
       
   122                         output_name = "%s%d_%s"%(type, block.getlocalId(), parameter)
       
   123                     output_value = [(output_name, output_info)]
       
   124                 return generator.ExtractModifier(variable, output_value, output_info)
       
   125     elif block_infos["type"] == "functionBlock":
       
   126         if not generator.ComputedBlocks.get(block, False) and not order:
       
   127             generator.ComputedBlocks[block] = True
       
   128             vars = []
       
   129             for i, variable in enumerate(block.inputVariables.getvariable()):
       
   130                 input_info = (generator.TagName, "block", block.getlocalId(), "input", i)
       
   131                 connections = variable.connectionPointIn.getconnections()
       
   132                 if connections is not None:
       
   133                     parameter = variable.getformalParameter()
       
   134                     value = generator.ComputeExpression(body, connections, executionOrderId > 0, inout_variables.has_key(parameter))
       
   135                     vars.append([(parameter, input_info),
       
   136                                  (" := ", ())] + generator.ExtractModifier(variable, value, input_info))
       
   137             generator.Program += [(generator.CurrentIndent, ()), 
       
   138                                   (name, (generator.TagName, "block", block.getlocalId(), "name")),
       
   139                                   ("(", ())]
       
   140             generator.Program += JoinList([(", ", ())], vars)
       
   141             generator.Program += [(");\n", ())]
       
   142         if link:
       
   143             connectionPoint = link.getposition()[-1]
       
   144         else:
       
   145             connectionPoint = None
       
   146         for i, variable in enumerate(block.outputVariables.getvariable()):
       
   147             blockPointx, blockPointy = variable.connectionPointOut.getrelPositionXY()
       
   148             if not connectionPoint or block.getx() + blockPointx == connectionPoint.getx() and block.gety() + blockPointy == connectionPoint.gety():
       
   149                 output_info = (generator.TagName, "block", block.getlocalId(), "output", i)
       
   150                 output_name = generator.ExtractModifier(variable, [("%s.%s"%(name, variable.getformalParameter()), output_info)], output_info)
       
   151                 if to_inout:
       
   152                     variable_name = "%s_%s"%(name, variable.getformalParameter())
       
   153                     if not generator.IsAlreadyDefined(variable_name):
       
   154                         if generator.Interface[-1][0] != "VAR" or generator.Interface[-1][1] is not None or generator.Interface[-1][2]:
       
   155                             generator.Interface.append(("VAR", None, False, []))
       
   156                         if variable.connectionPointOut in generator.ConnectionTypes:
       
   157                             generator.Interface[-1][3].append((generator.ConnectionTypes[variable.connectionPointOut], variable_name, None, None))
       
   158                         else:
       
   159                             generator.Interface[-1][3].append(("ANY", variable_name, None, None))
       
   160                         generator.Program += [(generator.CurrentIndent, ()),
       
   161                                               ("%s := "%variable_name, ())]
       
   162                         generator.Program += output_name
       
   163                         generator.Program += [(";\n", ())]
       
   164                     return [(variable_name, ())]
       
   165                 return output_name 
       
   166     if link is not None:
       
   167         raise ValueError, _("No output variable found")
       
   168 
       
   169 def initialise_block(type, name, block = None):
       
   170     return [(type, name, None, None)]
       
   171 
       
   172 #-------------------------------------------------------------------------------
       
   173 #                        Function Block Types definitions
       
   174 #-------------------------------------------------------------------------------
       
   175 
       
   176 
       
   177 """
       
   178 Ordored list of common Function Blocks defined in the IEC 61131-3
       
   179 Each block have this attributes:
       
   180     - "name" : The block name
       
   181     - "type" : The block type. It can be "function", "functionBlock" or "program"
       
   182     - "extensible" : Boolean that define if the block is extensible
       
   183     - "inputs" : List of the block inputs
       
   184     - "outputs" : List of the block outputs
       
   185     - "comment" : Comment that will be displayed in the block popup
       
   186     - "generate" : Method that generator will call for generating ST block code
       
   187 Inputs and outputs are a tuple of characteristics that are in order:
       
   188     - The name
       
   189     - The data type
       
   190     - The default modifier which can be "none", "negated", "rising" or "falling"
       
   191 """
       
   192 
       
   193 BlockTypes = [{"name" : _("Standard function blocks"), "list":
       
   194                [{"name" : "SR", "type" : "functionBlock", "extensible" : False, 
       
   195                     "inputs" : [("S1","BOOL","none"),("R","BOOL","none")], 
       
   196                     "outputs" : [("Q1","BOOL","none")],
       
   197                     "comment" : _("SR bistable\nThe SR bistable is a latch where the Set dominates."),
       
   198                     "generate" : generate_block, "initialise" : initialise_block},
       
   199                 {"name" : "RS", "type" : "functionBlock", "extensible" : False, 
       
   200                     "inputs" : [("S","BOOL","none"),("R1","BOOL","none")], 
       
   201                     "outputs" : [("Q1","BOOL","none")],
       
   202                     "comment" : _("RS bistable\nThe RS bistable is a latch where the Reset dominates."),
       
   203                     "generate" : generate_block, "initialise" : initialise_block},
       
   204                 {"name" : "SEMA", "type" : "functionBlock", "extensible" : False, 
       
   205                     "inputs" : [("CLAIM","BOOL","none"),("RELEASE","BOOL","none")], 
       
   206                     "outputs" : [("BUSY","BOOL","none")],
       
   207                     "comment" : _("Semaphore\nThe semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources."),
       
   208                     "generate" : generate_block, "initialise" : initialise_block},
       
   209                 {"name" : "R_TRIG", "type" : "functionBlock", "extensible" : False, 
       
   210                     "inputs" : [("CLK","BOOL","none")], 
       
   211                     "outputs" : [("Q","BOOL","none")],
       
   212                     "comment" : _("Rising edge detector\nThe output produces a single pulse when a rising edge is detected."),
       
   213                     "generate" : generate_block, "initialise" : initialise_block},
       
   214                 {"name" : "F_TRIG", "type" : "functionBlock", "extensible" : False, 
       
   215                     "inputs" : [("CLK","BOOL","none")], 
       
   216                     "outputs" : [("Q","BOOL","none")],
       
   217                     "comment" : _("Falling edge detector\nThe output produces a single pulse when a falling edge is detected."),
       
   218                     "generate" : generate_block, "initialise" : initialise_block},
       
   219                 {"name" : "CTU", "type" : "functionBlock", "extensible" : False, 
       
   220                     "inputs" : [("CU","BOOL","rising"),("R","BOOL","none"),("PV","INT","none")], 
       
   221                     "outputs" : [("Q","BOOL","none"),("CV","INT","none")],
       
   222                     "comment" : _("Up-counter\nThe up-counter can be used to signal when a count has reached a maximum value."),
       
   223                     "generate" : generate_block, "initialise" : initialise_block},
       
   224                 {"name" : "CTD", "type" : "functionBlock", "extensible" : False, 
       
   225                     "inputs" : [("CD","BOOL","rising"),("LD","BOOL","none"),("PV","INT","none")], 
       
   226                     "outputs" : [("Q","BOOL","none"),("CV","INT","none")],
       
   227                     "comment" : _("Down-counter\nThe down-counter can be used to signal when a count has reached zero, on counting down from a preset value."),
       
   228                     "generate" : generate_block, "initialise" : initialise_block},
       
   229                 {"name" : "CTUD", "type" : "functionBlock", "extensible" : False, 
       
   230                     "inputs" : [("CU","BOOL","rising"),("CD","BOOL","rising"),("R","BOOL","none"),("LD","BOOL","none"),("PV","INT","none")], 
       
   231                     "outputs" : [("QU","BOOL","none"),("QD","BOOL","none"),("CV","INT","none")],
       
   232                     "comment" : _("Up-down counter\nThe up-down counter has two inputs CU and CD. It can be used to both count up on one input and down on the other."),
       
   233                     "generate" : generate_block, "initialise" : initialise_block},
       
   234                 {"name" : "TP", "type" : "functionBlock", "extensible" : False, 
       
   235                     "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], 
       
   236                     "outputs" : [("Q","BOOL","none"),("ET","TIME","none")],
       
   237                     "comment" : _("Pulse timer\nThe pulse timer can be used to generate output pulses of a given time duration."),
       
   238                     "generate" : generate_block, "initialise" : initialise_block},
       
   239                 {"name" : "TON", "type" : "functionBlock", "extensible" : False, 
       
   240                     "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], 
       
   241                     "outputs" : [("Q","BOOL","none"),("ET","TIME","none")],
       
   242                     "comment" : _("On-delay timer\nThe on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true."),
       
   243                     "generate" : generate_block, "initialise" : initialise_block},
       
   244                 {"name" : "TOF", "type" : "functionBlock", "extensible" : False, 
       
   245                     "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], 
       
   246                     "outputs" : [("Q","BOOL","none"),("ET","TIME","none")],
       
   247                     "comment" : _("Off-delay timer\nThe off-delay timer can be used to delay setting an output false, for fixed period after input goes false."),
       
   248                     "generate" : generate_block, "initialise" : initialise_block},
       
   249                 ]},
       
   250               {"name" : _("Additional function blocks"), "list":
       
   251                [{"name" : "RTC", "type" : "functionBlock", "extensible" : False, 
       
   252                     "inputs" : [("IN","BOOL","none"),("PDT","DATE_AND_TIME","none")], 
       
   253                     "outputs" : [("Q","BOOL","none"),("CDT","DATE_AND_TIME","none")],
       
   254                     "comment" : _("Real time clock\nThe real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on."),
       
   255                     "generate" : generate_block, "initialise" : initialise_block},
       
   256                 {"name" : "INTEGRAL", "type" : "functionBlock", "extensible" : False, 
       
   257                     "inputs" : [("RUN","BOOL","none"),("R1","BOOL","none"),("XIN","REAL","none"),("X0","REAL","none"),("CYCLE","TIME","none")], 
       
   258                     "outputs" : [("Q","BOOL","none"),("XOUT","REAL","none")],
       
   259                     "comment" : _("Integral\nThe integral function block integrates the value of input XIN over time."),
       
   260                     "generate" : generate_block, "initialise" : initialise_block},
       
   261                 {"name" : "DERIVATIVE", "type" : "functionBlock", "extensible" : False, 
       
   262                     "inputs" : [("RUN","BOOL","none"),("XIN","REAL","none"),("CYCLE","TIME","none")], 
       
   263                     "outputs" : [("XOUT","REAL","none")],
       
   264                     "comment" : _("Derivative\nThe derivative function block produces an output XOUT proportional to the rate of change of the input XIN."),
       
   265                     "generate" : generate_block, "initialise" : initialise_block},
       
   266                 {"name" : "PID", "type" : "functionBlock", "extensible" : False, 
       
   267                     "inputs" : [("AUTO","BOOL","none"),("PV","REAL","none"),("SP","REAL","none"),("X0","REAL","none"),("KP","REAL","none"),("TR","REAL","none"),("TD","REAL","none"),("CYCLE","TIME","none")], 
       
   268                     "outputs" : [("XOUT","REAL","none")],
       
   269                     "comment" : _("PID\nThe PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control."),
       
   270                     "generate" : generate_block, "initialise" : initialise_block},
       
   271                 {"name" : "RAMP", "type" : "functionBlock", "extensible" : False, 
       
   272                     "inputs" : [("RUN","BOOL","none"),("X0","REAL","none"),("X1","REAL","none"),("TR","TIME","none"),("CYCLE","TIME","none")], 
       
   273                     "outputs" : [("BUSY","BOOL","none"),("XOUT","REAL","none")],
       
   274                     "comment" : _("Ramp\nThe RAMP function block is modelled on example given in the standard."),
       
   275                     "generate" : generate_block, "initialise" : initialise_block},
       
   276                 {"name" : "HYSTERESIS", "type" : "functionBlock", "extensible" : False, 
       
   277                     "inputs" : [("XIN1","REAL","none"),("XIN2","REAL","none"),("EPS","REAL","none")], 
       
   278                     "outputs" : [("Q","BOOL","none")],
       
   279                     "comment" : _("Hysteresis\nThe hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2."),
       
   280                     "generate" : generate_block, "initialise" : initialise_block},
       
   281 ##                {"name" : "RATIO_MONITOR", "type" : "functionBlock", "extensible" : False, 
       
   282 ##                    "inputs" : [("PV1","REAL","none"),("PV2","REAL","none"),("RATIO","REAL","none"),("TIMON","TIME","none"),("TIMOFF","TIME","none"),("TOLERANCE","BOOL","none"),("RESET","BOOL","none"),("CYCLE","TIME","none")], 
       
   283 ##                    "outputs" : [("ALARM","BOOL","none"),("TOTAL_ERR","BOOL","none")],
       
   284 ##                    "comment" : _("Ratio monitor\nThe ratio_monitor function block checks that one process value PV1 is always a given ratio (defined by input RATIO) of a second process value PV2."),
       
   285 ##                    "generate" : generate_block, "initialise" : initialise_block}
       
   286                 ]},
       
   287              ]
       
   288 
       
   289 
       
   290 #-------------------------------------------------------------------------------
       
   291 #                           Data Types definitions
       
   292 #-------------------------------------------------------------------------------
       
   293 
       
   294 """
       
   295 Ordored list of common data types defined in the IEC 61131-3
       
   296 Each type is associated to his direct parent type. It defines then a hierarchy
       
   297 between type that permits to make a comparison of two types
       
   298 """
       
   299 TypeHierarchy_list = [
       
   300     ("ANY", None),
       
   301     ("ANY_DERIVED", "ANY"),
       
   302     ("ANY_ELEMENTARY", "ANY"),
       
   303     ("ANY_MAGNITUDE", "ANY_ELEMENTARY"),
       
   304     ("ANY_BIT", "ANY_ELEMENTARY"),
       
   305     ("ANY_NBIT", "ANY_BIT"),
       
   306     ("ANY_STRING", "ANY_ELEMENTARY"),
       
   307     ("ANY_DATE", "ANY_ELEMENTARY"),
       
   308     ("ANY_NUM", "ANY_MAGNITUDE"),
       
   309     ("ANY_REAL", "ANY_NUM"),
       
   310     ("ANY_INT", "ANY_NUM"),
       
   311     ("ANY_SINT", "ANY_INT"),
       
   312     ("ANY_UINT", "ANY_INT"),
       
   313     ("BOOL", "ANY_BIT"),
       
   314     ("SINT", "ANY_SINT"),
       
   315     ("INT", "ANY_SINT"),
       
   316     ("DINT", "ANY_SINT"),
       
   317     ("LINT", "ANY_SINT"),
       
   318     ("USINT", "ANY_UINT"),
       
   319     ("UINT", "ANY_UINT"),
       
   320     ("UDINT", "ANY_UINT"),
       
   321     ("ULINT", "ANY_UINT"),
       
   322     ("REAL", "ANY_REAL"),
       
   323     ("LREAL", "ANY_REAL"),
       
   324     ("TIME", "ANY_MAGNITUDE"),
       
   325     ("DATE", "ANY_DATE"),
       
   326     ("TOD", "ANY_DATE"),
       
   327     ("DT", "ANY_DATE"),
       
   328     ("STRING", "ANY_STRING"),
       
   329     ("BYTE", "ANY_NBIT"),
       
   330     ("WORD", "ANY_NBIT"),
       
   331     ("DWORD", "ANY_NBIT"),
       
   332     ("LWORD", "ANY_NBIT")
       
   333     #("WSTRING", "ANY_STRING") # TODO
       
   334 ]
       
   335 
       
   336 TypeHierarchy = dict(TypeHierarchy_list)
       
   337 
       
   338 """
       
   339 returns true if the given data type is the same that "reference" meta-type or one of its types.
       
   340 """
       
   341 def IsOfType(type, reference):
       
   342     if reference is None:
       
   343         return True
       
   344     elif type == reference:
       
   345         return True
       
   346     else:
       
   347         parent_type = TypeHierarchy[type]
       
   348         if parent_type is not None:
       
   349             return IsOfType(parent_type, reference)
       
   350     return False
       
   351 
       
   352 """
       
   353 returns list of all types that correspont to the ANY* meta type
       
   354 """
       
   355 def GetSubTypes(type):
       
   356     return [typename for typename, parenttype in TypeHierarchy.items() if not typename.startswith("ANY") and IsOfType(typename, type)]
       
   357 
       
   358 
       
   359 DataTypeRange_list = [
       
   360     ("SINT", (-2**7, 2**7 - 1)),
       
   361     ("INT", (-2**15, 2**15 - 1)),
       
   362     ("DINT", (-2**31, 2**31 - 1)),
       
   363     ("LINT", (-2**31, 2**31 - 1)),
       
   364     ("USINT", (0, 2**8 - 1)),
       
   365     ("UINT", (0, 2**16 - 1)),
       
   366     ("UDINT", (0, 2**31 - 1)),
       
   367     ("ULINT", (0, 2**31 - 1))
       
   368 ]
       
   369 
       
   370 DataTypeRange = dict(DataTypeRange_list)
       
   371 
       
   372 
       
   373 
       
   374 #-------------------------------------------------------------------------------
       
   375 #                             Test identifier
       
   376 #-------------------------------------------------------------------------------
       
   377 
       
   378 
       
   379 
       
   380 # Test if identifier is valid
       
   381 def TestIdentifier(identifier):
       
   382      if identifier[0].isdigit():
       
   383         return False
       
   384      words = identifier.split('_')
       
   385      for i, word in enumerate(words):
       
   386          if len(word) == 0 and i != 0:
       
   387              return False
       
   388          if len(word) != 0 and not word.isalnum():
       
   389              return False
       
   390      return True
       
   391 
       
   392 
       
   393 #-------------------------------------------------------------------------------
       
   394 #                        Standard functions list generation
       
   395 #-------------------------------------------------------------------------------
       
   396 
       
   397 
       
   398 """
       
   399 take a .csv file and translate it it a "csv_table"
       
   400 """            
       
   401 def csv_file_to_table(file):
       
   402     return [ map(string.strip,line.split(';')) for line in file.xreadlines()]
       
   403 
       
   404 """
       
   405 seek into the csv table to a section ( section_name match 1st field )
       
   406 return the matching row without first field
       
   407 """
       
   408 def find_section(section_name, table):
       
   409     fields = [None]
       
   410     while(fields[0] != section_name):
       
   411         fields = table.pop(0)
       
   412     return fields[1:]
       
   413 
       
   414 """
       
   415 extract the standard functions standard parameter names and types...
       
   416 return a { ParameterName: Type, ...}
       
   417 """
       
   418 def get_standard_funtions_input_variables(table):
       
   419     variables = find_section("Standard_functions_variables_types", table)
       
   420     standard_funtions_input_variables = {}
       
   421     fields = [True,True]
       
   422     while(fields[1]):
       
   423         fields = table.pop(0)
       
   424         variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ!=''])
       
   425         standard_funtions_input_variables[variable_from_csv['name']] = variable_from_csv['type']
       
   426     return standard_funtions_input_variables
       
   427     
       
   428 """
       
   429 translate .csv file input declaration into PLCOpenEditor interessting values
       
   430 in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...}
       
   431 return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")] 
       
   432 """
       
   433 def csv_input_translate(str_decl, variables, base):
       
   434     decl = str_decl.replace('(','').replace(')','').replace(' ','').split(',')
       
   435     params = []
       
   436     
       
   437     len_of_not_predifined_variable = len([True for param_type in decl if param_type not in variables])
       
   438     
       
   439     for param_type in decl:
       
   440         if param_type in variables.keys():
       
   441             param_name = param_type
       
   442             param_type = variables[param_type]
       
   443         elif len_of_not_predifined_variable > 1:
       
   444             param_name = "IN%d"%base
       
   445             base += 1
       
   446         else:
       
   447             param_name = "IN"
       
   448         params.append((param_name, param_type, "none"))
       
   449     return params
       
   450 
       
   451 
       
   452 ANY_TO_ANY_LIST=[
       
   453         # simple type conv are let as C cast
       
   454         (("ANY_INT","ANY_BIT"),("ANY_NUM","ANY_BIT"), ("return_type", "__move_", "IN_type")),
       
   455         (("ANY_REAL",),("ANY_REAL",), ("return_type", "__move_", "IN_type")),
       
   456         # REAL_TO_INT
       
   457         (("ANY_REAL",),("ANY_SINT",), ("return_type", "__real_to_sint", None)),
       
   458         (("ANY_REAL",),("ANY_UINT",), ("return_type", "__real_to_uint", None)),
       
   459         (("ANY_REAL",),("ANY_BIT",), ("return_type", "__real_to_bit", None)),
       
   460         # TO_TIME
       
   461         (("ANY_INT","ANY_BIT"),("ANY_DATE","TIME"), ("return_type", "__int_to_time", None)),
       
   462         (("ANY_REAL",),("ANY_DATE","TIME"), ("return_type", "__real_to_time", None)),
       
   463         (("ANY_STRING",), ("ANY_DATE","TIME"), ("return_type", "__string_to_time", None)),
       
   464         # FROM_TIME
       
   465         (("ANY_DATE","TIME"), ("ANY_REAL",), ("return_type", "__time_to_real", None)),
       
   466         (("ANY_DATE","TIME"), ("ANY_INT","ANY_NBIT"), ("return_type", "__time_to_int", None)),
       
   467         (("TIME",), ("ANY_STRING",), ("return_type", "__time_to_string", None)),
       
   468         (("DATE",), ("ANY_STRING",), ("return_type", "__date_to_string", None)),
       
   469         (("TOD",), ("ANY_STRING",), ("return_type", "__tod_to_string", None)),
       
   470         (("DT",), ("ANY_STRING",), ("return_type", "__dt_to_string", None)),
       
   471         # TO_STRING
       
   472         (("BOOL",), ("ANY_STRING",), ("return_type", "__bool_to_string", None)),
       
   473         (("ANY_BIT",), ("ANY_STRING",), ("return_type", "__bit_to_string", None)),
       
   474         (("ANY_REAL",), ("ANY_STRING",), ("return_type", "__real_to_string", None)),
       
   475         (("ANY_SINT",), ("ANY_STRING",), ("return_type", "__sint_to_string", None)),
       
   476         (("ANY_UINT",), ("ANY_STRING",), ("return_type", "__uint_to_string", None)),
       
   477         # FROM_STRING
       
   478         (("ANY_STRING",), ("BOOL",), ("return_type", "__string_to_bool", None)),
       
   479         (("ANY_STRING",), ("ANY_BIT",), ("return_type", "__string_to_bit", None)),
       
   480         (("ANY_STRING",), ("ANY_SINT",), ("return_type", "__string_to_sint", None)),
       
   481         (("ANY_STRING",), ("ANY_UINT",), ("return_type", "__string_to_uint", None)),
       
   482         (("ANY_STRING",), ("ANY_REAL",), ("return_type", "__string_to_real", None))]
       
   483 
       
   484 
       
   485 BCD_TO_ANY_LIST=[
       
   486         (("BYTE",),("USINT",), ("return_type", "__bcd_to_uint", None)),
       
   487         (("WORD",),("UINT",), ("return_type", "__bcd_to_uint", None)),
       
   488         (("DWORD",),("UDINT",), ("return_type", "__bcd_to_uint", None)),
       
   489         (("LWORD",),("ULINT",), ("return_type", "__bcd_to_uint", None))]
       
   490 
       
   491 
       
   492 ANY_TO_BCD_LIST=[
       
   493         (("USINT",),("BYTE",), ("return_type", "__uint_to_bcd", None)),
       
   494         (("UINT",),("WORD",), ("return_type", "__uint_to_bcd", None)),
       
   495         (("UDINT",),("DWORD",), ("return_type", "__uint_to_bcd", None)),
       
   496         (("ULINT",),("LWORD",), ("return_type", "__uint_to_bcd", None))]
       
   497 
       
   498 
       
   499 def ANY_TO_ANY_FORMAT_GEN(any_to_any_list, fdecl):
       
   500 
       
   501     for (InTypes, OutTypes, Format) in any_to_any_list:
       
   502         outs = reduce(lambda a,b: a or b, map(lambda testtype : IsOfType(fdecl["outputs"][0][1],testtype), OutTypes))
       
   503         inps = reduce(lambda a,b: a or b, map(lambda testtype : IsOfType(fdecl["inputs"][0][1],testtype), InTypes))
       
   504         if inps and outs and fdecl["outputs"][0][1] != fdecl["inputs"][0][1]:
       
   505              return Format
       
   506     
       
   507     return None
       
   508 
       
   509 
       
   510 """
       
   511 Returns this kind of declaration for all standard functions
       
   512 
       
   513             [{"name" : "Numerical", 'list': [   {   
       
   514                 'baseinputnumber': 1,
       
   515                 'comment': 'Addition',
       
   516                 'extensible': True,
       
   517                 'inputs': [   ('IN1', 'ANY_NUM', 'none'),
       
   518                               ('IN2', 'ANY_NUM', 'none')],
       
   519                 'name': 'ADD',
       
   520                 'outputs': [('OUT', 'ANY_NUM', 'none')],
       
   521                 'type': 'function'}, ...... ] },.....]
       
   522 """
       
   523 def get_standard_funtions(table):
       
   524     
       
   525     variables = get_standard_funtions_input_variables(table)
       
   526     
       
   527     fonctions = find_section("Standard_functions_type",table)
       
   528 
       
   529     Standard_Functions_Decl = []
       
   530     Current_section = None
       
   531     
       
   532     translate = {
       
   533             "extensible" : lambda x: {"yes":True, "no":False}[x],
       
   534             "inputs" : lambda x:csv_input_translate(x,variables,baseinputnumber),
       
   535             "outputs":lambda x:[("OUT",x,"none")]}
       
   536     
       
   537     for fields in table:
       
   538         if fields[1]:
       
   539             # If function section name given
       
   540             if fields[0]:
       
   541                 words = fields[0].split('"')
       
   542                 if len(words) > 1:
       
   543                     section_name = words[1]
       
   544                 else:
       
   545                     section_name = fields[0]
       
   546                 Current_section = {"name" : section_name, "list" : []}
       
   547                 Standard_Functions_Decl.append(Current_section)
       
   548                 Function_decl_list = []
       
   549             if Current_section:
       
   550                 Function_decl = dict([(champ, val) for champ, val in zip(fonctions, fields[1:]) if champ])
       
   551                 Function_decl["generate"] = generate_block
       
   552                 Function_decl["initialise"] = lambda x,y:[]
       
   553                 baseinputnumber = int(Function_decl.get("baseinputnumber",1))
       
   554                 Function_decl["baseinputnumber"] = baseinputnumber
       
   555                 for param, value in Function_decl.iteritems():
       
   556                     if param in translate:
       
   557                         Function_decl[param] = translate[param](value)
       
   558                 Function_decl["type"] = "function"
       
   559                 
       
   560                 if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*') :
       
   561                     input_ovrloading_types = GetSubTypes(Function_decl["inputs"][0][1])
       
   562                     output_types = GetSubTypes(Function_decl["outputs"][0][1])
       
   563                 else:
       
   564                     input_ovrloading_types = [None]
       
   565                     output_types = [None]
       
   566                 
       
   567                 funcdeclname_orig = Function_decl["name"]
       
   568                 funcdeclname = Function_decl["name"].strip('*_')
       
   569                 fdc = Function_decl["inputs"][:]
       
   570                 for intype in input_ovrloading_types:
       
   571                     if intype != None:
       
   572                         Function_decl["inputs"] = []
       
   573                         for decl_tpl in fdc:
       
   574                             if IsOfType(intype, decl_tpl[1]):
       
   575                                 Function_decl["inputs"] += [(decl_tpl[0], intype, decl_tpl[2])]
       
   576                             else:
       
   577                                 Function_decl["inputs"] += [(decl_tpl)]
       
   578                             
       
   579                             if funcdeclname_orig.startswith('*'):
       
   580                                 funcdeclin = intype + '_' + funcdeclname 
       
   581                             else:
       
   582                                 funcdeclin = funcdeclname
       
   583                     else:
       
   584                         funcdeclin = funcdeclname
       
   585                         
       
   586                     for outype in output_types:
       
   587                         if outype != None:
       
   588                             decl_tpl = Function_decl["outputs"][0]
       
   589                             Function_decl["outputs"] = [ (decl_tpl[0] , outype,  decl_tpl[2])]
       
   590                             if funcdeclname_orig.endswith('*'):
       
   591                                 funcdeclout =  funcdeclin + '_' + outype
       
   592                             else:
       
   593                                 funcdeclout =  funcdeclin
       
   594                         else:
       
   595                             funcdeclout =  funcdeclin
       
   596                         Function_decl["name"] = funcdeclout
       
   597 
       
   598 
       
   599                         fdecl = Function_decl
       
   600                         res = eval(Function_decl["python_eval_c_code_format"])
       
   601 
       
   602                         if res != None :
       
   603                             # create the copy of decl dict to be appended to section
       
   604                             Function_decl_copy = Function_decl.copy()
       
   605                             Current_section["list"].append(Function_decl_copy)
       
   606             else:
       
   607                 raise "First function must be in a category"
       
   608     
       
   609     return Standard_Functions_Decl
       
   610 
       
   611 std_decl = get_standard_funtions(csv_file_to_table(open(os.path.join(os.path.split(__file__)[0],"iec_std.csv"))))#, True)
       
   612 
       
   613 BlockTypes.extend(std_decl)
       
   614 
       
   615 for section in BlockTypes: 
       
   616     for desc in section["list"]:
       
   617         words = desc["comment"].split('"')
       
   618         if len(words) > 1:
       
   619             desc["comment"] = words[1]
       
   620         desc["usage"] = (
       
   621             "\n (" +
       
   622             str([ " " + fctdecl[1]+":"+fctdecl[0] for fctdecl in desc["inputs"]]).strip("[]").replace("'",'') +
       
   623             " ) => (" +
       
   624             str([ " " + fctdecl[1]+":"+fctdecl[0] for fctdecl in desc["outputs"]]).strip("[]").replace("'",'') +
       
   625             " )")
       
   626 
       
   627 
       
   628 #-------------------------------------------------------------------------------
       
   629 #                            Languages Keywords
       
   630 #-------------------------------------------------------------------------------
       
   631 
       
   632 
       
   633 # Keywords for Pou Declaration
       
   634 POU_BLOCK_START_KEYWORDS = ["FUNCTION", "FUNCTION_BLOCK", "PROGRAM"]
       
   635 POU_BLOCK_END_KEYWORDS = ["END_FUNCTION", "END_FUNCTION_BLOCK", "END_PROGRAM"]
       
   636 POU_KEYWORDS = ["EN", "ENO", "F_EDGE", "R_EDGE"] + POU_BLOCK_START_KEYWORDS + POU_BLOCK_END_KEYWORDS
       
   637 for category in BlockTypes:
       
   638     for block in category["list"]:
       
   639         if block["name"] not in POU_KEYWORDS:
       
   640             POU_KEYWORDS.append(block["name"])
       
   641 
       
   642 
       
   643 # Keywords for Type Declaration
       
   644 TYPE_BLOCK_START_KEYWORDS = ["TYPE", "STRUCT"]
       
   645 TYPE_BLOCK_END_KEYWORDS = ["END_TYPE", "END_STRUCT"]
       
   646 TYPE_KEYWORDS = ["ARRAY", "OF", "T", "D", "TIME_OF_DAY", "DATE_AND_TIME"] + TYPE_BLOCK_START_KEYWORDS + TYPE_BLOCK_END_KEYWORDS
       
   647 TYPE_KEYWORDS.extend([keyword for keyword in TypeHierarchy.keys() if keyword not in TYPE_KEYWORDS])
       
   648 
       
   649 
       
   650 # Keywords for Variable Declaration
       
   651 VAR_BLOCK_START_KEYWORDS = ["VAR", "VAR_INPUT", "VAR_OUTPUT", "VAR_IN_OUT", "VAR_TEMP", "VAR_EXTERNAL"]
       
   652 VAR_BLOCK_END_KEYWORDS = ["END_VAR"]
       
   653 VAR_KEYWORDS = ["AT", "CONSTANT", "RETAIN", "NON_RETAIN"] + VAR_BLOCK_START_KEYWORDS + VAR_BLOCK_END_KEYWORDS
       
   654 
       
   655 
       
   656 # Keywords for Configuration Declaration
       
   657 CONFIG_BLOCK_START_KEYWORDS = ["CONFIGURATION", "RESOURCE", "VAR_ACCESS", "VAR_CONFIG", "VAR_GLOBAL"]
       
   658 CONFIG_BLOCK_END_KEYWORDS = ["END_CONFIGURATION", "END_RESOURCE", "END_VAR"]
       
   659 CONFIG_KEYWORDS = ["ON", "PROGRAM", "WITH", "READ_ONLY", "READ_WRITE", "TASK"] + CONFIG_BLOCK_START_KEYWORDS + CONFIG_BLOCK_END_KEYWORDS
       
   660 
       
   661 # Keywords for Structured Function Chart
       
   662 SFC_BLOCK_START_KEYWORDS = ["ACTION", "INITIAL_STEP", "STEP", "TRANSITION"]
       
   663 SFC_BLOCK_END_KEYWORDS = ["END_ACTION", "END_STEP", "END_TRANSITION"]
       
   664 SFC_KEYWORDS = ["FROM", "TO"] + SFC_BLOCK_START_KEYWORDS + SFC_BLOCK_START_KEYWORDS
       
   665 
       
   666 
       
   667 # Keywords for Instruction List
       
   668 IL_KEYWORDS = ["TRUE", "FALSE", "LD", "LDN", "ST", "STN", "S", "R", "AND", "ANDN", "OR", "ORN",
       
   669  "XOR", "XORN", "NOT", "ADD", "SUB", "MUL", "DIV", "MOD", "GT", "GE", "EQ", "NE",
       
   670  "LE", "LT", "JMP", "JMPC", "JMPCN", "CAL", "CALC", "CALCN", "RET", "RETC", "RETCN"]
       
   671 
       
   672 
       
   673 # Keywords for Structured Text
       
   674 ST_BLOCK_START_KEYWORDS = ["IF", "ELSIF", "ELSE", "CASE", "FOR", "WHILE", "REPEAT"]
       
   675 ST_BLOCK_END_KEYWORDS = ["END_IF", "END_CASE", "END_FOR", "END_WHILE", "END_REPEAT"]
       
   676 ST_KEYWORDS = ["TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", 
       
   677  "RETURN", "NOT", "MOD", "AND", "XOR", "OR"] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS
       
   678 
       
   679 # All the keywords of IEC
       
   680 IEC_BLOCK_START_KEYWORDS = []
       
   681 IEC_BLOCK_END_KEYWORDS = []
       
   682 IEC_KEYWORDS = ["E", "TRUE", "FALSE"]
       
   683 for all_keywords, keywords_list in [(IEC_BLOCK_START_KEYWORDS, [POU_BLOCK_START_KEYWORDS, TYPE_BLOCK_START_KEYWORDS,
       
   684                                                                 VAR_BLOCK_START_KEYWORDS, CONFIG_BLOCK_START_KEYWORDS,
       
   685                                                                 SFC_BLOCK_START_KEYWORDS, ST_BLOCK_START_KEYWORDS]),
       
   686                                     (IEC_BLOCK_END_KEYWORDS, [POU_BLOCK_END_KEYWORDS, TYPE_BLOCK_END_KEYWORDS,
       
   687                                                               VAR_BLOCK_END_KEYWORDS, CONFIG_BLOCK_END_KEYWORDS,
       
   688                                                               SFC_BLOCK_END_KEYWORDS, ST_BLOCK_END_KEYWORDS]),
       
   689                                     (IEC_KEYWORDS, [POU_KEYWORDS, TYPE_KEYWORDS, VAR_KEYWORDS, CONFIG_KEYWORDS,
       
   690                                                     SFC_KEYWORDS, IL_KEYWORDS, ST_KEYWORDS])]:
       
   691     for keywords in keywords_list:
       
   692         all_keywords.extend([keyword for keyword in keywords if keyword not in all_keywords])
       
   693