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